From d745efbf878a4ec8031ad8f13981eacf295bd7c4 Mon Sep 17 00:00:00 2001 From: Ixniy Evonniy Date: Wed, 1 Jul 2026 01:19:32 +0300 Subject: [PATCH] fix: sing-box status styling --- cache.db | Bin 32768 -> 32768 bytes main.jai | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- win32.jai | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 2 deletions(-) diff --git a/cache.db b/cache.db index 904b1c8b3118472d8bba741109284374d72e6f88..89d9d502c73c6f4d60c067b06b0dc836cd79905c 100644 GIT binary patch delta 58 zcmZo@U}|V!n&2Ry!vF!TF5hx_-%9sy7F5{4KS{trKo=^pXj-CXtLK|VV2MQv2>{0x B5QYE% delta 58 zcmZo@U}|V!n&2Ry#Q*`$8^j}i$EgKw7F5{4KS{trKpQIYM9aZ``t^qoz!HlT5&*Rq B5P|>z diff --git a/main.jai b/main.jai index 4f209e4..abbef4b 100644 --- a/main.jai +++ b/main.jai @@ -590,8 +590,7 @@ show_context_menu :: (app: *App_State) { if !hMenu return; defer DestroyMenu(hMenu); - status_str := ifx app.singbox_running then "Sing-box: Running" else "Sing-box: Stopped"; - AppendMenuW(hMenu, MF_STRING | MF_GRAYED | MF_DISABLED, CMD_STATUS, utf8_to_wide(status_str)); + AppendMenuW(hMenu, MF_OWNERDRAW, CMD_STATUS, null); AppendMenuW(hMenu, MF_SEPARATOR, 0, null); @@ -802,6 +801,89 @@ app_wnd_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESUL SetWindowLongPtrW(hwnd, GWLP_USERDATA, cast(LONG_PTR) app); return 0; } + case WM_MEASUREITEM; { + lpmis := cast(*MEASUREITEMSTRUCT) lparam; + if lpmis.CtlType == ODT_MENU { + lpmis.itemWidth = 160; + lpmis.itemHeight = 22; + return TRUE; + } + } + case WM_DRAWITEM; { + lpdis := cast(*DRAWITEMSTRUCT) lparam; + if lpdis.CtlType == ODT_MENU { + if app { + hDC := lpdis.hDC; + rect := lpdis.rcItem; + + // Draw menu background + hMenuBrush := GetSysColorBrush(COLOR_MENU); + FillRect(hDC, *rect, hMenuBrush); + + // Create a bold Segoe UI font for high readability + face_name := utf8_to_wide("Segoe UI"); + hBoldFont := CreateFontW( + -13, 0, 0, 0, FW_BOLD, 0, 0, 0, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, face_name + ); + hOldFont := SelectObject(hDC, hBoldFont); + defer { + SelectObject(hDC, hOldFont); + DeleteObject(hBoldFont); + } + + // Status and status dot color selection + status_text := ""; + dot_color: u32 = 0; + + if app.is_connecting { + status_text = "Sing-box: Connecting..."; + dot_color = RGB(241, 196, 15); // Yellow/Orange + } else if app.is_updating { + status_text = "Sing-box: Updating..."; + dot_color = RGB(241, 196, 15); // Yellow/Orange + } else if app.singbox_running { + status_text = "Sing-box: Running"; + dot_color = RGB(39, 174, 96); // Green + } else { + status_text = "Sing-box: Stopped"; + dot_color = RGB(219, 68, 85); // Red + } + + // Draw status dot (filled circle) in GDI in the 16x16 area + // Centered 10x10 dot at x: rect.left+9, y: rect.top+6 + hBrush := CreateSolidBrush(dot_color); + hOldBrush := SelectObject(hDC, hBrush); + + hPen := CreatePen(PS_NULL, 0, 0); + hOldPen := SelectObject(hDC, hPen); + + dot_left := rect.left + 9; + dot_top := rect.top + 6; + Ellipse(hDC, dot_left, dot_top, dot_left + 10, dot_top + 10); + + SelectObject(hDC, hOldPen); + DeleteObject(hPen); + SelectObject(hDC, hOldBrush); + DeleteObject(hBrush); + + // Draw status text next to the dot (using standard menu text color, which is highly readable) + text_color := GetSysColor(COLOR_MENUTEXT); + SetTextColor(hDC, text_color); + SetBkMode(hDC, TRANSPARENT); + + text_rect: RECT; + text_rect.left = rect.left + 28; + text_rect.top = rect.top + 3; + text_rect.right = rect.right; + text_rect.bottom = rect.bottom; + + DrawTextW(hDC, utf8_to_wide(status_text), -1, *text_rect, DT_SINGLELINE | DT_VCENTER); + } + return TRUE; + } + } case WM_TRAY_CALLBACK; { if lparam == WM_RBUTTONUP || lparam == WM_LBUTTONUP { show_context_menu(app); diff --git a/win32.jai b/win32.jai index 6e7c7f1..19faaa5 100644 --- a/win32.jai +++ b/win32.jai @@ -47,6 +47,52 @@ MF_DISABLED : u32 : 0x00000002; MF_SEPARATOR : u32 : 0x00000800; MF_CHECKED : u32 : 0x00000008; MF_POPUP : u32 : 0x00000010; +MF_OWNERDRAW : u32 : 0x00000100; + +ODT_MENU :: 1; +COLOR_MENU :: 4; +TRANSPARENT :: 1; +DT_SINGLELINE :: 0x00000020; +DT_VCENTER :: 0x00000004; +DI_NORMAL :: 0x0003; + +PS_NULL :: 5; +COLOR_MENUTEXT :: 7; +FW_BOLD :: 700; +DEFAULT_CHARSET :: 1; +OUT_DEFAULT_PRECIS :: 0; +CLIP_DEFAULT_PRECIS :: 0; +CLEARTYPE_QUALITY :: 5; +DEFAULT_PITCH :: 0; +FF_DONTCARE :: 0; + +WM_MEASUREITEM :: 0x002C; +WM_DRAWITEM :: 0x002B; + +HPEN :: *void; +HFONT :: *void; + +// Structs +DRAWITEMSTRUCT :: struct { + CtlType: u32; + CtlID: u32; + itemID: u32; + itemAction: u32; + itemState: u32; + hwndItem: HWND; + hDC: HDC; + rcItem: RECT; + itemData: u64; +} + +MEASUREITEMSTRUCT :: struct { + CtlType: u32; + CtlID: u32; + itemID: u32; + itemWidth: u32; + itemHeight: u32; + itemData: u64; +} // NIM constants NIM_ADD : u32 : 0x00000000; @@ -70,6 +116,31 @@ SetForegroundWindow :: (hWnd: HWND) -> BOOL #foreign user32; EnableWindow :: (hWnd: HWND, bEnable: BOOL) -> BOOL #foreign user32; PostMessageW :: (hWnd: HWND, Msg: u32, wParam: WPARAM, lParam: LPARAM) -> BOOL #foreign user32; +SetTextColor :: (hdc: HDC, color: u32) -> u32 #foreign gdi32; +SetBkMode :: (hdc: HDC, mode: s32) -> s32 #foreign gdi32; +DrawTextW :: (hdc: HDC, lpchText: *u16, cchText: s32, lprc: *RECT, format: u32) -> s32 #foreign user32; +GetSysColorBrush :: (nIndex: s32) -> HBRUSH #foreign user32; +FillRect :: (hDC: HDC, lprc: *RECT, hbr: HBRUSH) -> s32 #foreign user32; +DrawIconEx :: (hdc: HDC, xLeft: s32, yTop: s32, hIcon: HICON, cxWidth: s32, cyWidth: s32, istepIfAniCur: u32, hbrFlickerFreeDraw: HBRUSH, diFlags: u32) -> BOOL #foreign user32; +CreateFontW :: ( + nHeight: s32, + nWidth: s32, + nEscapement: s32, + nOrientation: s32, + fnWeight: s32, + fdwItalic: DWORD, + fdwUnderline: DWORD, + fdwStrikeOut: DWORD, + fdwCharSet: DWORD, + fdwOutputPrecision: DWORD, + fdwClipPrecision: DWORD, + fdwQuality: DWORD, + fdwPitchAndFamily: DWORD, + lpszFace: *u16 +) -> HFONT #foreign gdi32; +CreatePen :: (iStyle: s32, cWidth: s32, color: u32) -> HPEN #foreign gdi32; +Ellipse :: (hdc: HDC, left: s32, top: s32, right: s32, bottom: s32) -> BOOL #foreign gdi32; +GetSysColor :: (nIndex: s32) -> u32 #foreign user32; // Custom message IDs WM_USER :: 0x0400; WM_TRAY_CALLBACK :: WM_USER + 1; @@ -280,6 +351,10 @@ set_windows_system_proxy :: (enable: bool, server: string) -> bool { return true; } +RGB :: (r: u32, g: u32, b: u32) -> u32 { + return r | (g << 8) | (b << 16); +} +