summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-03 23:26:50 +0100
committersalmonik <salmonik@tuta.io>2026-11-03 23:26:50 +0100
commita1a8281a6f72ee7f1bcedf442f6ade48da8ffef4 (patch)
tree07a55046e125b752047da22abe65258feeada5e8
parentWorld: add world/sky material colour modulation (diff)
downloadhl2h-a1a8281a6f72ee7f1bcedf442f6ade48da8ffef4.tar.gz
hl2h-a1a8281a6f72ee7f1bcedf442f6ade48da8ffef4.zip
GUI: add inline colour picker control
Replace the six RGB sliders on the World tint card with a CK_COLOR control: each row shows a live swatch and expands an inline picker with a saturation/value square, a hue bar, and hex/RGB preview. The picker grows the card in place so it scrolls/clips like any other row, keeps HSV state while open (grey does not lose its hue), writes the shared 0-255 RGB triple used by the config, and saves on mouse release. Costs ~1.2k small rects per frame only while a picker is open; no texture interface needed.
Diffstat (limited to '')
-rw-r--r--CONTEXT.md7
-rw-r--r--src/gui.cpp326
2 files changed, 319 insertions, 14 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index ce4a02e..b48100b 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -144,6 +144,13 @@ vocabulary / decisions.
144 `x`, `y`, `category` and an `expanded` bitmask (card index order). Every 144 `x`, `y`, `category` and an `expanded` bitmask (card index order). Every
145 feature now persists an `active` and `bind` pair, including movement, melee 145 feature now persists an `active` and `bind` pair, including movement, melee
146 and spread. 146 and spread.
147- **Colour control (`CK_COLOR`)**: row shows a live swatch; clicking the row
148 expands an inline picker (saturation/value square + hue bar + hex/RGB
149 preview) instead of three RGB sliders, growing the card in place so it
150 scrolls and clips like any other row. The picker keeps HSV state so grey
151 colours do not lose their hue; dragging writes the 0-255 RGB triple shared
152 with the config and saves on mouse release. Only one picker is open at a
153 time; closing the GUI or collapsing the card hides it.
147- Config writes are atomic: `config.tmp` is written and flushed, then 154- Config writes are atomic: `config.tmp` is written and flushed, then
148 `rename()`d over the real file. A failed write raises an error toast. 155 `rename()`d over the real file. A failed write raises an error toast.
149 156
diff --git a/src/gui.cpp b/src/gui.cpp
index 9d27a09..9981f25 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -182,7 +182,8 @@ enum CtrlKind {
182 CK_BIND, 182 CK_BIND,
183 CK_SECTION, 183 CK_SECTION,
184 CK_INFO, 184 CK_INFO,
185 CK_BUTTON 185 CK_BUTTON,
186 CK_COLOR
186}; 187};
187 188
188enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET }; 189enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET };
@@ -193,9 +194,13 @@ struct Ctrl {
193 const char *desc; 194 const char *desc;
194 bool *bval; // CK_TOGGLE 195 bool *bval; // CK_TOGGLE
195 int *ival; // CK_SLIDER 196 int *ival; // CK_SLIDER
197 int *rgb; // CK_COLOR: three ints, 0..255
196 int min, max; 198 int min, max;
197 int action; // CK_BUTTON 199 int action; // CK_BUTTON
198 bool zero_unlimited; // slider: value 0 displays as "All" 200 bool zero_unlimited; // slider: value 0 displays as "All"
201 // CK_COLOR picker state, kept while the picker is open so grey colours do
202 // not lose their hue when the user drags value/saturation.
203 float hue, sat, val;
199}; 204};
200 205
201struct Card { 206struct Card {
@@ -247,6 +252,10 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc,
247 c.max = max; 252 c.max = max;
248 c.action = action; 253 c.action = action;
249 c.zero_unlimited = false; 254 c.zero_unlimited = false;
255 c.rgb = nullptr;
256 c.hue = 0.0f;
257 c.sat = 0.0f;
258 c.val = 1.0f;
250 g_cards[card].n++; 259 g_cards[card].n++;
251} 260}
252static void add_toggle(int card, const char *label, const char *desc, 261static void add_toggle(int card, const char *label, const char *desc,
@@ -267,6 +276,12 @@ static void add_button(int card, const char *label, const char *desc,
267 int action) { 276 int action) {
268 add_ctrl(card, CK_BUTTON, label, desc, 0, 0, nullptr, nullptr, action); 277 add_ctrl(card, CK_BUTTON, label, desc, 0, 0, nullptr, nullptr, action);
269} 278}
279static void add_color(int card, const char *label, const char *desc,
280 int *rgb) {
281 int i = g_nctrl;
282 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE);
283 g_ctrls[i].rgb = rgb;
284}
270 285
271static int ctrl_val(const Ctrl &c) { 286static int ctrl_val(const Ctrl &c) {
272 if (c.kind == CK_TOGGLE && c.bval) 287 if (c.kind == CK_TOGGLE && c.bval)
@@ -381,19 +396,11 @@ static void build_cards() {
381 add_toggle(tint, "World brushes", 396 add_toggle(tint, "World brushes",
382 "Tint world textures (sky and water excluded from this part).", 397 "Tint world textures (sky and water excluded from this part).",
383 &g_tint_world_on); 398 &g_tint_world_on);
384 add_slider(tint, "World red", "Red channel of the world tint (0-255).", 0, 399 add_color(tint, "World colour", "Click the swatch to open the picker.",
385 255, &g_tint_world_rgb[0]); 400 g_tint_world_rgb);
386 add_slider(tint, "World green", "Green channel of the world tint (0-255).", 0,
387 255, &g_tint_world_rgb[1]);
388 add_slider(tint, "World blue", "Blue channel of the world tint (0-255).", 0,
389 255, &g_tint_world_rgb[2]);
390 add_toggle(tint, "Skybox", "Tint the skybox textures.", &g_tint_sky_on); 401 add_toggle(tint, "Skybox", "Tint the skybox textures.", &g_tint_sky_on);
391 add_slider(tint, "Sky red", "Red channel of the sky tint (0-255).", 0, 255, 402 add_color(tint, "Sky colour", "Click the swatch to open the picker.",
392 &g_tint_sky_rgb[0]); 403 g_tint_sky_rgb);
393 add_slider(tint, "Sky green", "Green channel of the sky tint (0-255).", 0,
394 255, &g_tint_sky_rgb[1]);
395 add_slider(tint, "Sky blue", "Blue channel of the sky tint (0-255).", 0, 255,
396 &g_tint_sky_rgb[2]);
397 404
398 int spread = add_card(3, &g_spread, "Spread control", 405 int spread = add_card(3, &g_spread, "Spread control",
399 "Choose a server shot seed that tightens the cone.", 406 "Choose a server shot seed that tightens the cone.",
@@ -436,6 +443,8 @@ static int g_scroll_drag_dy = 0;
436static float g_scroll_drag_start = 0.0f; 443static float g_scroll_drag_start = 0.0f;
437static int g_hover_slider = -1; 444static int g_hover_slider = -1;
438static int g_slider_drag = -1; 445static int g_slider_drag = -1;
446static int g_picker_ctrl = -1; // CK_COLOR control with its picker expanded
447static int g_picker_drag = 0; // 1 = SV square, 2 = hue bar
439static int g_capture_card = -1; 448static int g_capture_card = -1;
440static QAngle g_frozen{0, 0, 0}; 449static QAngle g_frozen{0, 0, 0};
441static int g_mouse_x = 0, g_mouse_y = 0; 450static int g_mouse_x = 0, g_mouse_y = 0;
@@ -821,7 +830,40 @@ static int button_w(const char *label) {
821 return bw; 830 return bw;
822} 831}
823 832
833static int label_row_h(const Ctrl &c);
834static bool picker_open_for(const Ctrl &c);
835static int picker_panel_h();
836static bool card_in_category(int i);
837static Rect card_rect(int index, int y);
838static int card_h(const Card &c);
839
824static int row_h(const Ctrl &c) { 840static int row_h(const Ctrl &c) {
841 int h = label_row_h(c);
842 if (picker_open_for(c))
843 h += picker_panel_h();
844 return h;
845}
846
847// ---------------------------------------------------------------------------
848// Colour picker (CK_COLOR)
849// ---------------------------------------------------------------------------
850
851static constexpr int PICKER_SV_W = 180;
852static constexpr int PICKER_SV_H = 110;
853static constexpr int PICKER_HUE_H = 12;
854static constexpr int PICKER_GAP = 8;
855
856static bool picker_open_for(const Ctrl &c) {
857 return c.kind == CK_COLOR && g_picker_ctrl >= 0 &&
858 &c == &g_ctrls[g_picker_ctrl];
859}
860
861static int picker_panel_h() {
862 return S((float)(PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H +
863 PICKER_GAP));
864}
865
866static int label_row_h(const Ctrl &c) {
825 if (c.kind == CK_BUTTON) 867 if (c.kind == CK_BUTTON)
826 return S(32.0f); 868 return S(32.0f);
827 int maxw = (c.kind == CK_INFO || c.kind == CK_SECTION) 869 int maxw = (c.kind == CK_INFO || c.kind == CK_SECTION)
@@ -837,6 +879,207 @@ static int row_h(const Ctrl &c) {
837 return h < minh ? minh : h; 879 return h < minh ? minh : h;
838} 880}
839 881
882static void hsv_to_rgb(float h, float s, float v, int &r, int &g, int &b) {
883 if (h < 0.0f) h = 0.0f;
884 h = fmodf(h, 360.0f);
885 if (h < 0.0f) h += 360.0f;
886 if (s < 0.0f) s = 0.0f;
887 if (s > 1.0f) s = 1.0f;
888 if (v < 0.0f) v = 0.0f;
889 if (v > 1.0f) v = 1.0f;
890 float c = v * s;
891 float x = c * (1.0f - fabsf(fmodf(h / 60.0f, 2.0f) - 1.0f));
892 float m = v - c;
893 float rr = 0.0f, gg = 0.0f, bb = 0.0f;
894 if (h < 60.0f) {
895 rr = c; gg = x;
896 } else if (h < 120.0f) {
897 rr = x; gg = c;
898 } else if (h < 180.0f) {
899 gg = c; bb = x;
900 } else if (h < 240.0f) {
901 gg = x; bb = c;
902 } else if (h < 300.0f) {
903 rr = x; bb = c;
904 } else {
905 rr = c; bb = x;
906 }
907 r = (int)((rr + m) * 255.0f + 0.5f);
908 g = (int)((gg + m) * 255.0f + 0.5f);
909 b = (int)((bb + m) * 255.0f + 0.5f);
910}
911
912static void rgb_to_hsv(int r, int g, int b, float &h, float &s, float &v) {
913 float rf = r / 255.0f, gf = g / 255.0f, bf = b / 255.0f;
914 float mx = fmaxf(rf, fmaxf(gf, bf));
915 float mn = fminf(rf, fminf(gf, bf));
916 float d = mx - mn;
917 v = mx;
918 s = mx > 0.0f ? d / mx : 0.0f;
919 if (d <= 0.0f) {
920 h = 0.0f;
921 return;
922 }
923 if (mx == rf)
924 h = 60.0f * fmodf((gf - bf) / d, 6.0f);
925 else if (mx == gf)
926 h = 60.0f * ((bf - rf) / d + 2.0f);
927 else
928 h = 60.0f * ((rf - gf) / d + 4.0f);
929 if (h < 0.0f)
930 h += 360.0f;
931}
932
933struct PickerRects {
934 Rect panel, sv, hue, preview;
935};
936
937static PickerRects picker_rects(const Rect &cr, int row_y, int base_h) {
938 PickerRects p{};
939 int px = cr.x + S(16.0f);
940 int py = row_y + base_h;
941 int pw = card_content_w() - S(32.0f);
942 if (pw > S(340.0f)) pw = S(340.0f);
943 p.panel = Rect{px, py, pw, picker_panel_h()};
944 int preview_w = S(84.0f);
945 int svw = S((float)PICKER_SV_W);
946 if (svw + preview_w + S(16.0f) > pw)
947 svw = pw - preview_w - S(16.0f);
948 if (svw < S(60.0f))
949 svw = S(60.0f);
950 p.sv = Rect{px + S(PICKER_GAP), py + S(PICKER_GAP), svw,
951 S((float)PICKER_SV_H)};
952 p.hue = Rect{p.sv.x, p.sv.y + p.sv.h + S(PICKER_GAP), svw,
953 S((float)PICKER_HUE_H)};
954 p.preview = Rect{p.sv.x + p.sv.w + S(12.0f), p.sv.y, preview_w, S(52.0f)};
955 return p;
956}
957
958// Widget row/card used to map a control index back to screen space. Needed by
959// the drag handler, which runs outside click/layout context.
960static bool control_rect(int ctrl_index, Rect &out_card, Rect &out_row) {
961 Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f),
962 g_win_w - S(190.0f), g_win_h - S(38.0f) - S(30.0f)};
963 int y = content.y - (int)g_scroll + S(8.0f);
964 for (int i = 0; i < g_ncards; i++) {
965 if (!card_in_category(i))
966 continue;
967 Card &c = g_cards[i];
968 Rect cr = card_rect(i, y);
969 if (c.expanded) {
970 int by = cr.y + header_h(c) + S(6.0f);
971 for (int j = 0; j < c.n; j++) {
972 Ctrl &ct = g_ctrls[c.c0 + j];
973 int rh = row_h(ct);
974 if (c.c0 + j == ctrl_index) {
975 out_card = cr;
976 out_row = Rect{cr.x, by, cr.w, rh};
977 return true;
978 }
979 by += rh;
980 }
981 }
982 y += card_h(c);
983 }
984 return false;
985}
986
987static bool picker_rects_for(int ctrl_index, PickerRects &out) {
988 if (ctrl_index < 0 || ctrl_index >= g_nctrl)
989 return false;
990 Rect card, row;
991 if (!control_rect(ctrl_index, card, row))
992 return false;
993 Ctrl &ct = g_ctrls[ctrl_index];
994 out = picker_rects(card, row.y, label_row_h(ct));
995 return true;
996}
997
998static void picker_apply(Ctrl &ct, const PickerRects &p) {
999 if (!ct.rgb || p.sv.w <= 0 || p.hue.w <= 0)
1000 return;
1001 if (g_picker_drag == 1) {
1002 float s = (float)(g_mouse_x - p.sv.x) / (float)p.sv.w;
1003 float v = 1.0f - (float)(g_mouse_y - p.sv.y) / (float)p.sv.h;
1004 if (s < 0.0f) s = 0.0f;
1005 if (s > 1.0f) s = 1.0f;
1006 if (v < 0.0f) v = 0.0f;
1007 if (v > 1.0f) v = 1.0f;
1008 ct.sat = s;
1009 ct.val = v;
1010 } else if (g_picker_drag == 2) {
1011 float h = 360.0f * (float)(g_mouse_x - p.hue.x) / (float)p.hue.w;
1012 if (h < 0.0f) h = 0.0f;
1013 if (h >= 360.0f) h = 359.9f;
1014 ct.hue = h;
1015 }
1016 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1017}
1018
1019static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct,
1020 int row_y) {
1021 PickerRects p = picker_rects(cr, row_y, label_row_h(ct));
1022 fill(p.panel, Col{20, 23, 29, 250}, &clip);
1023 outline(p.panel, C_BORDER, &clip);
1024
1025 // Saturation/value field: solid rows of a white->hue gradient scaled by V.
1026 int sx = S(4.0f);
1027 int sy = S(4.0f);
1028 if (sx < 2) sx = 2;
1029 if (sy < 2) sy = 2;
1030 for (int yy = 0; yy < p.sv.h; yy += sy) {
1031 float v = 1.0f - ((float)yy + (float)sy * 0.5f) / (float)p.sv.h;
1032 int rh = sy < p.sv.h - yy ? sy : p.sv.h - yy;
1033 for (int xx = 0; xx < p.sv.w; xx += sx) {
1034 float s = ((float)xx + (float)sx * 0.5f) / (float)p.sv.w;
1035 int r, g, b;
1036 hsv_to_rgb(ct.hue, s, v, r, g, b);
1037 int rw = sx < p.sv.w - xx ? sx : p.sv.w - xx;
1038 fill(Rect{p.sv.x + xx, p.sv.y + yy, rw, rh}, Col{r, g, b, 255}, &clip);
1039 }
1040 }
1041 outline(p.sv, Col{0, 0, 0, 255}, &clip);
1042
1043 // Hue bar.
1044 int hx = S(4.0f);
1045 if (hx < 2) hx = 2;
1046 for (int xx = 0; xx < p.hue.w; xx += hx) {
1047 float h = 360.0f * ((float)xx + (float)hx * 0.5f) / (float)p.hue.w;
1048 int r, g, b;
1049 hsv_to_rgb(h, 1.0f, 1.0f, r, g, b);
1050 int rw = hx < p.hue.w - xx ? hx : p.hue.w - xx;
1051 fill(Rect{p.hue.x + xx, p.hue.y, rw, p.hue.h}, Col{r, g, b, 255}, &clip);
1052 }
1053 outline(p.hue, Col{0, 0, 0, 255}, &clip);
1054
1055 // Selection markers.
1056 int mx = p.sv.x + (int)(ct.sat * (float)p.sv.w);
1057 int my = p.sv.y + (int)((1.0f - ct.val) * (float)p.sv.h);
1058 Rect svm{mx - S(4.0f), my - S(4.0f), S(8.0f), S(8.0f)};
1059 outline(svm, Col{0, 0, 0, 255}, &clip);
1060 Rect svm2{mx - S(3.0f), my - S(3.0f), S(6.0f), S(6.0f)};
1061 outline(svm2, Col{255, 255, 255, 255}, &clip);
1062
1063 int hxm = p.hue.x + (int)((ct.hue / 360.0f) * (float)p.hue.w);
1064 line(hxm, p.hue.y - S(2.0f), hxm, p.hue.y + p.hue.h + S(2.0f),
1065 Col{255, 255, 255, 255});
1066
1067 // Preview swatch and hex value.
1068 int pr = ct.rgb ? ct.rgb[0] : 255;
1069 int pg = ct.rgb ? ct.rgb[1] : 255;
1070 int pb = ct.rgb ? ct.rgb[2] : 255;
1071 fill(p.preview, Col{pr, pg, pb, 255}, &clip);
1072 outline(p.preview, C_BORDER, &clip);
1073 char hex[16];
1074 snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb);
1075 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f), hex,
1076 g_font_body, g_body_h, p.preview.w, C_TEXT);
1077 char rgb[48];
1078 snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb);
1079 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f) + g_body_h,
1080 rgb, g_font_body, g_body_h, p.preview.w, C_DIM);
1081}
1082
840static int body_h(const Card &c) { 1083static int body_h(const Card &c) {
841 if (!c.expanded) 1084 if (!c.expanded)
842 return 0; 1085 return 0;
@@ -877,6 +1120,8 @@ static void close_gui() {
877 g_dragging = false; 1120 g_dragging = false;
878 g_scroll_drag = false; 1121 g_scroll_drag = false;
879 g_slider_drag = -1; 1122 g_slider_drag = -1;
1123 g_picker_ctrl = -1;
1124 g_picker_drag = 0;
880 g_reset_armed = false; 1125 g_reset_armed = false;
881 input_clear_mouse_states(); 1126 input_clear_mouse_states();
882 if (g_sdl_ready) { 1127 if (g_sdl_ready) {
@@ -1115,6 +1360,37 @@ static void handle_mouse_click() {
1115 } 1360 }
1116 return; 1361 return;
1117 } 1362 }
1363 if (ct.kind == CK_COLOR) {
1364 int idx = c.c0 + j;
1365 int base = label_row_h(ct);
1366 bool is_open = (g_picker_ctrl == idx);
1367 if (is_open) {
1368 PickerRects p = picker_rects(cr, by, base);
1369 if (p.sv.has(mx, my)) {
1370 g_picker_drag = 1;
1371 picker_apply(g_ctrls[idx], p);
1372 return;
1373 }
1374 if (p.hue.has(mx, my)) {
1375 g_picker_drag = 2;
1376 picker_apply(g_ctrls[idx], p);
1377 return;
1378 }
1379 }
1380 Rect sw{cr.x + cr.w - S(96.0f), by + (base - S(20.0f)) / 2,
1381 S(72.0f), S(20.0f)};
1382 if (my < by + base || sw.has(mx, my)) {
1383 if (is_open) {
1384 g_picker_ctrl = -1;
1385 } else {
1386 g_picker_ctrl = idx;
1387 if (ct.rgb)
1388 rgb_to_hsv(ct.rgb[0], ct.rgb[1], ct.rgb[2], ct.hue, ct.sat,
1389 ct.val);
1390 }
1391 }
1392 return;
1393 }
1118 if (ct.kind == CK_BUTTON) { 1394 if (ct.kind == CK_BUTTON) {
1119 int bw = button_w(ct.label); 1395 int bw = button_w(ct.label);
1120 Rect b{cr.x + cr.w - S(16.0f) - bw, 1396 Rect b{cr.x + cr.w - S(16.0f) - bw,
@@ -1291,6 +1567,17 @@ void gui_handle_input() {
1291 } 1567 }
1292 } 1568 }
1293 1569
1570 if (g_picker_drag) {
1571 if (!g_mouse_down) {
1572 g_picker_drag = 0;
1573 config_save();
1574 } else if (g_picker_ctrl >= 0) {
1575 PickerRects p;
1576 if (picker_rects_for(g_picker_ctrl, p))
1577 picker_apply(g_ctrls[g_picker_ctrl], p);
1578 }
1579 }
1580
1294 if (g_scroll_drag) { 1581 if (g_scroll_drag) {
1295 if (!g_mouse_down) { 1582 if (!g_mouse_down) {
1296 g_scroll_drag = false; 1583 g_scroll_drag = false;
@@ -1472,7 +1759,8 @@ static void draw_card(const Rect &clip, int index, int y) {
1472 wrap_text(ct.label, maxw, g_body_h, lines); 1759 wrap_text(ct.label, maxw, g_body_h, lines);
1473 int lh = g_body_h + S(2.0f); 1760 int lh = g_body_h + S(2.0f);
1474 int text_h = (int)lines.size() * lh - S(2.0f); 1761 int text_h = (int)lines.size() * lh - S(2.0f);
1475 int ly = by + (rh - text_h) / 2; 1762 int base_h = label_row_h(ct);
1763 int ly = by + (base_h - text_h) / 2;
1476 draw_wrapped(clip, cr.x + S(16.0f), ly, ct.label, g_font_body, g_body_h, 1764 draw_wrapped(clip, cr.x + S(16.0f), ly, ct.label, g_font_body, g_body_h,
1477 maxw, C_TEXT, S(2.0f)); 1765 maxw, C_TEXT, S(2.0f));
1478 if (ct.kind == CK_TOGGLE) { 1766 if (ct.kind == CK_TOGGLE) {
@@ -1483,6 +1771,16 @@ static void draw_card(const Rect &clip, int index, int y) {
1483 on ? C_GOOD : C_DIM); 1771 on ? C_GOOD : C_DIM);
1484 } else if (ct.kind == CK_SLIDER) { 1772 } else if (ct.kind == CK_SLIDER) {
1485 draw_slider(clip, cr, by, rh, ct); 1773 draw_slider(clip, cr, by, rh, ct);
1774 } else if (ct.kind == CK_COLOR) {
1775 int pr = ct.rgb ? ct.rgb[0] : 255;
1776 int pg = ct.rgb ? ct.rgb[1] : 255;
1777 int pb = ct.rgb ? ct.rgb[2] : 255;
1778 Rect sw{cr.x + cr.w - S(96.0f),
1779 by + (base_h - S(20.0f)) / 2, S(72.0f), S(20.0f)};
1780 fill(sw, Col{pr, pg, pb, 255}, &clip);
1781 outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip);
1782 if (picker_open_for(ct))
1783 draw_picker(clip, cr, ct, by);
1486 } 1784 }
1487 } 1785 }
1488 by += rh; 1786 by += rh;