Editor theme and bug fixes.

This commit is contained in:
Justin Marshall
2026-05-15 10:32:24 -07:00
parent ea14ecb595
commit f6b489b2ff
18 changed files with 5972 additions and 455 deletions
+650 -21
View File
@@ -28,6 +28,598 @@ void Select_Ungroup();
IMPLEMENT_DYNAMIC(CEntityDlg, CDialog)
#ifndef BS_TYPEMASK
#define BS_TYPEMASK 0x0000000F
#endif
static const COLORREF ENTITY_DARK_BG = RGB(17, 19, 23);
static const COLORREF ENTITY_DARK_PANEL = RGB(24, 27, 32);
static const COLORREF ENTITY_DARK_PANEL_ALT = RGB(29, 33, 39);
static const COLORREF ENTITY_DARK_FIELD = RGB(14, 16, 20);
static const COLORREF ENTITY_DARK_FIELD_ALT = RGB(18, 21, 26);
static const COLORREF ENTITY_DARK_PROP_NAME = RGB(31, 36, 44);
static const COLORREF ENTITY_DARK_PROP_NAME_SEL = RGB(54, 76, 105);
static const COLORREF ENTITY_DARK_BORDER = RGB(55, 62, 72);
static const COLORREF ENTITY_DARK_BORDER_HOT = RGB(87, 166, 255);
static const COLORREF ENTITY_DARK_TEXT = RGB(226, 232, 240);
static const COLORREF ENTITY_DARK_TEXT_MUTED = RGB(151, 163, 184);
static const COLORREF ENTITY_DARK_TEXT_DISABLED = RGB(103, 112, 128);
static const COLORREF ENTITY_DARK_ACCENT = RGB(87, 166, 255);
static const char* ENTITY_DARK_BUTTON_OLDPROC = "IceTech.Entity.DarkButtonOldProc";
static const char* ENTITY_DARK_PROP_OLDPROC = "IceTech.Entity.DarkPropertyOldProc";
static const char* ENTITY_DARK_PROP_DIVIDER = "IceTech.Entity.DarkPropertyDivider";
static const char* ENTITY_DARK_FIELD_OLDPROC = "IceTech.Entity.DarkFieldOldProc";
static const char* ENTITY_DARK_COMBO_OLDPROC = "IceTech.Entity.DarkComboOldProc";
static void EntityDark_FillRect(HDC hDC, const RECT& rc, COLORREF color) {
HBRUSH brush = ::CreateSolidBrush(color);
::FillRect(hDC, &rc, brush);
::DeleteObject(brush);
}
static void EntityDark_FrameRect(HDC hDC, const RECT& rc, COLORREF color) {
HBRUSH brush = ::CreateSolidBrush(color);
::FrameRect(hDC, &rc, brush);
::DeleteObject(brush);
}
static void EntityDark_DrawButton(HWND hWnd, HDC hDC) {
RECT rc;
::GetClientRect(hWnd, &rc);
const UINT style = (UINT)::GetWindowLong(hWnd, GWL_STYLE);
const UINT type = style & BS_TYPEMASK;
const bool isCheck = (type == BS_CHECKBOX || type == BS_AUTOCHECKBOX || type == BS_3STATE || type == BS_AUTO3STATE);
const bool enabled = ::IsWindowEnabled(hWnd) ? true : false;
const bool pushed = (::SendMessage(hWnd, BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
const bool checked = (::SendMessage(hWnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
char text[256];
text[0] = '\0';
::GetWindowTextA(hWnd, text, sizeof(text));
HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0);
HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL;
::SetBkMode(hDC, TRANSPARENT);
::SetTextColor(hDC, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
if (isCheck) {
EntityDark_FillRect(hDC, rc, ENTITY_DARK_PANEL);
RECT box = rc;
box.left += 3;
box.right = box.left + 14;
box.top += max(0, (rc.bottom - rc.top - 14) / 2);
box.bottom = box.top + 14;
EntityDark_FillRect(hDC, box, enabled ? ENTITY_DARK_FIELD : RGB(20, 22, 26));
EntityDark_FrameRect(hDC, box, checked ? ENTITY_DARK_ACCENT : ENTITY_DARK_BORDER);
if (checked) {
HPEN pen = ::CreatePen(PS_SOLID, 2, enabled ? ENTITY_DARK_ACCENT : ENTITY_DARK_TEXT_DISABLED);
HPEN oldPen = (HPEN)::SelectObject(hDC, pen);
::MoveToEx(hDC, box.left + 3, box.top + 7, NULL);
::LineTo(hDC, box.left + 6, box.top + 10);
::LineTo(hDC, box.right - 3, box.top + 3);
::SelectObject(hDC, oldPen);
::DeleteObject(pen);
}
RECT textRect = rc;
textRect.left = box.right + 7;
textRect.right -= 2;
::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
else {
COLORREF face = pushed ? RGB(22, 25, 30) : ENTITY_DARK_PANEL_ALT;
EntityDark_FillRect(hDC, rc, face);
EntityDark_FrameRect(hDC, rc, enabled ? ENTITY_DARK_BORDER : RGB(39, 44, 52));
RECT inner = rc;
::InflateRect(&inner, -1, -1);
if (!pushed) {
EntityDark_FrameRect(hDC, inner, RGB(35, 40, 47));
}
RECT textRect = rc;
if (pushed) {
::OffsetRect(&textRect, 1, 1);
}
::DrawTextA(hDC, text, -1, &textRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
}
if (oldFont) {
::SelectObject(hDC, oldFont);
}
}
static LRESULT CALLBACK EntityDarkButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC);
if (!oldProc) {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = ::BeginPaint(hWnd, &ps);
EntityDark_DrawButton(hWnd, hDC);
::EndPaint(hWnd, &ps);
return 0;
}
case WM_PRINTCLIENT:
EntityDark_DrawButton(hWnd, (HDC)wParam);
return 0;
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_ENABLE:
case WM_SETTEXT:
case BM_SETCHECK:
case BM_SETSTATE:
{
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
::InvalidateRect(hWnd, NULL, FALSE);
return result;
}
case WM_NCDESTROY:
{
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
::RemovePropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC);
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
}
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
static void EntityDark_SubclassButton(CWnd& wnd) {
HWND hWnd = wnd.GetSafeHwnd();
if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC)) {
return;
}
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
::SetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC, (HANDLE)oldProc);
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkButtonProc);
::InvalidateRect(hWnd, NULL, TRUE);
}
static void EntityDark_ApplyNativeTheme(HWND hWnd) {
if (!hWnd) {
return;
}
typedef HRESULT(WINAPI* SetWindowThemeProc)(HWND, LPCWSTR, LPCWSTR);
typedef BOOL(WINAPI* AllowDarkModeForWindowProc)(HWND, BOOL);
static HMODULE hUxTheme = ::LoadLibraryA("uxtheme.dll");
static SetWindowThemeProc pSetWindowTheme = hUxTheme ? (SetWindowThemeProc)::GetProcAddress(hUxTheme, "SetWindowTheme") : NULL;
static AllowDarkModeForWindowProc pAllowDarkModeForWindow = hUxTheme ? (AllowDarkModeForWindowProc)::GetProcAddress(hUxTheme, MAKEINTRESOURCEA(133)) : NULL;
if (pAllowDarkModeForWindow) {
pAllowDarkModeForWindow(hWnd, TRUE);
}
if (pSetWindowTheme) {
pSetWindowTheme(hWnd, L"DarkMode_Explorer", NULL);
}
::SendMessage(hWnd, WM_THEMECHANGED, 0, 0);
}
static void EntityDark_RemoveClientEdge(HWND hWnd) {
if (!hWnd) {
return;
}
LONG exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
if (exStyle & WS_EX_CLIENTEDGE) {
::SetWindowLong(hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_CLIENTEDGE);
::SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
}
static void EntityDark_DrawFrame(HWND hWnd, COLORREF color) {
HDC hDC = ::GetWindowDC(hWnd);
if (!hDC) {
return;
}
RECT rc;
::GetWindowRect(hWnd, &rc);
::OffsetRect(&rc, -rc.left, -rc.top);
EntityDark_FrameRect(hDC, rc, color);
::ReleaseDC(hWnd, hDC);
}
static LRESULT CALLBACK EntityDarkFieldProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC);
if (!oldProc) {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_NCPAINT:
case WM_PAINT:
{
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
EntityDark_DrawFrame(hWnd, (::GetFocus() == hWnd) ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER);
return result;
}
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_ENABLE:
{
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
::RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
return result;
}
case WM_NCDESTROY:
{
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
::RemovePropA(hWnd, ENTITY_DARK_FIELD_OLDPROC);
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
}
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
static void EntityDark_DrawCombo(HWND hWnd, HDC hDC) {
RECT rc;
::GetClientRect(hWnd, &rc);
const bool enabled = ::IsWindowEnabled(hWnd) ? true : false;
const bool focused = (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? true : false;
EntityDark_FillRect(hDC, rc, enabled ? ENTITY_DARK_FIELD : RGB(20, 22, 26));
EntityDark_FrameRect(hDC, rc, focused ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER);
RECT arrow = rc;
arrow.left = arrow.right - 22;
EntityDark_FillRect(hDC, arrow, ENTITY_DARK_PANEL_ALT);
EntityDark_FrameRect(hDC, arrow, ENTITY_DARK_BORDER);
POINT pts[3];
int cx = arrow.left + (arrow.right - arrow.left) / 2;
int cy = arrow.top + (arrow.bottom - arrow.top) / 2 + 1;
pts[0].x = cx - 4; pts[0].y = cy - 2;
pts[1].x = cx + 4; pts[1].y = cy - 2;
pts[2].x = cx; pts[2].y = cy + 3;
HBRUSH arrowBrush = ::CreateSolidBrush(enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
HBRUSH oldBrush = (HBRUSH)::SelectObject(hDC, arrowBrush);
HPEN arrowPen = ::CreatePen(PS_SOLID, 1, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
HPEN oldPen = (HPEN)::SelectObject(hDC, arrowPen);
::Polygon(hDC, pts, 3);
::SelectObject(hDC, oldPen);
::DeleteObject(arrowPen);
::SelectObject(hDC, oldBrush);
::DeleteObject(arrowBrush);
char text[512];
text[0] = '\0';
int sel = (int)::SendMessage(hWnd, CB_GETCURSEL, 0, 0);
if (sel >= 0) {
::SendMessageA(hWnd, CB_GETLBTEXT, (WPARAM)sel, (LPARAM)text);
}
else {
::GetWindowTextA(hWnd, text, sizeof(text));
}
HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0);
HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL;
::SetBkMode(hDC, TRANSPARENT);
::SetTextColor(hDC, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
RECT textRect = rc;
textRect.left += 7;
textRect.right = arrow.left - 3;
::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
if (oldFont) {
::SelectObject(hDC, oldFont);
}
}
static LRESULT CALLBACK EntityDarkComboProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC);
if (!oldProc) {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
{
UINT comboType = (UINT)::GetWindowLong(hWnd, GWL_STYLE) & 0x00000003;
if (comboType == CBS_DROPDOWNLIST) {
PAINTSTRUCT ps;
HDC hDC = ::BeginPaint(hWnd, &ps);
EntityDark_DrawCombo(hWnd, hDC);
::EndPaint(hWnd, &ps);
return 0;
}
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
EntityDark_DrawFrame(hWnd, (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER);
return result;
}
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_ENABLE:
case WM_SETTEXT:
case CB_SETCURSEL:
case CB_ADDSTRING:
case CB_DELETESTRING:
case CB_RESETCONTENT:
{
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
::InvalidateRect(hWnd, NULL, FALSE);
return result;
}
case WM_NCDESTROY:
{
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
::RemovePropA(hWnd, ENTITY_DARK_COMBO_OLDPROC);
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
}
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
static void EntityDark_SubclassField(CWnd& wnd) {
HWND hWnd = wnd.GetSafeHwnd();
if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC)) {
return;
}
EntityDark_RemoveClientEdge(hWnd);
EntityDark_ApplyNativeTheme(hWnd);
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
::SetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC, (HANDLE)oldProc);
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkFieldProc);
::InvalidateRect(hWnd, NULL, TRUE);
}
static void EntityDark_SubclassCombo(CWnd& wnd) {
HWND hWnd = wnd.GetSafeHwnd();
if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC)) {
return;
}
EntityDark_RemoveClientEdge(hWnd);
EntityDark_ApplyNativeTheme(hWnd);
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
::SetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC, (HANDLE)oldProc);
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkComboProc);
::InvalidateRect(hWnd, NULL, TRUE);
}
static void EntityDark_SetPropertyListRowHeight(HWND hWnd) {
if (!hWnd) {
return;
}
// The old resource-sized rows were too tight after the dark-theme font pass.
// Give every row enough breathing room so descenders (g, y, p, q) are not clipped at the bottom.
::SendMessage(hWnd, LB_SETITEMHEIGHT, 0, 32);
}
static void EntityDark_CopyPropText(char* out, int outSize, CPropertyItem* propItem, int index, HWND hWnd, bool wantValue) {
if (!out || outSize <= 0) {
return;
}
out[0] = '\0';
if (propItem && propItem != (CPropertyItem*)LB_ERR) {
const char* src = wantValue ? propItem->m_curValue : propItem->m_propName;
if (src && src[0]) {
idStr::Copynz(out, src, outSize);
return;
}
}
char text[2048];
text[0] = '\0';
::SendMessageA(hWnd, LB_GETTEXT, (WPARAM)index, (LPARAM)text);
char* tab = strchr(text, '\t');
if (tab) {
if (wantValue) {
idStr::Copynz(out, tab + 1, outSize);
}
else {
*tab = '\0';
idStr::Copynz(out, text, outSize);
}
}
else if (!wantValue) {
idStr::Copynz(out, text, outSize);
}
}
static void EntityDark_DrawPropertyList(HWND hWnd, HDC hDC) {
RECT client;
::GetClientRect(hWnd, &client);
EntityDark_FillRect(hDC, client, ENTITY_DARK_FIELD);
const int count = (int)::SendMessage(hWnd, LB_GETCOUNT, 0, 0);
if (count <= 0) {
return;
}
int divider = (int)(INT_PTR)::GetPropA(hWnd, ENTITY_DARK_PROP_DIVIDER);
if (divider <= 0) {
divider = 170;
}
if (divider > client.right - 80) {
divider = max(80, client.right / 2);
}
const int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0);
const int curSel = (int)::SendMessage(hWnd, LB_GETCURSEL, 0, 0);
const bool focused = (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? true : false;
HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0);
HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL;
::SetBkMode(hDC, TRANSPARENT);
for (int i = max(0, topIndex); i < count; i++) {
RECT item;
if ((int)::SendMessage(hWnd, LB_GETITEMRECT, (WPARAM)i, (LPARAM)&item) == LB_ERR) {
break;
}
if (item.top >= client.bottom) {
break;
}
if (item.bottom <= client.top) {
continue;
}
const bool selected = (i == curSel);
RECT rowRect = item;
rowRect.left = client.left;
rowRect.right = client.right;
EntityDark_FillRect(hDC, rowRect, selected ? RGB(31, 41, 55) : ENTITY_DARK_FIELD);
RECT nameRect = item;
nameRect.left = client.left;
nameRect.right = min(client.right, divider);
EntityDark_FillRect(hDC, nameRect, selected ? ENTITY_DARK_PROP_NAME_SEL : ENTITY_DARK_PROP_NAME);
RECT valueRect = item;
valueRect.left = min(client.right, divider + 1);
valueRect.right = client.right;
EntityDark_FillRect(hDC, valueRect, selected ? RGB(30, 43, 59) : ENTITY_DARK_FIELD);
CPropertyItem* propItem = (CPropertyItem*)::SendMessage(hWnd, LB_GETITEMDATA, (WPARAM)i, 0);
char nameText[1024];
char valueText[2048];
EntityDark_CopyPropText(nameText, sizeof(nameText), propItem, i, hWnd, false);
EntityDark_CopyPropText(valueText, sizeof(valueText), propItem, i, hWnd, true);
RECT nameTextRect = nameRect;
nameTextRect.left += 10;
nameTextRect.right -= 10;
nameTextRect.top += 3;
nameTextRect.bottom -= 1;
RECT valueTextRect = valueRect;
valueTextRect.left += 12;
valueTextRect.right -= 10;
valueTextRect.top += 3;
valueTextRect.bottom -= 1;
::SetTextColor(hDC, selected ? RGB(255, 255, 255) : ENTITY_DARK_TEXT);
::DrawTextA(hDC, nameText, -1, &nameTextRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
::SetTextColor(hDC, selected ? RGB(235, 245, 255) : RGB(206, 214, 226));
::DrawTextA(hDC, valueText, -1, &valueTextRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
// Dark, vertical-only split. Do not draw horizontal row dividers; they were cutting through the value text.
RECT dividerRect = item;
dividerRect.left = min(client.right - 1, divider);
dividerRect.right = dividerRect.left + 1;
EntityDark_FillRect(hDC, dividerRect, RGB(42, 48, 58));
}
if (focused) {
RECT focusRect = client;
::InflateRect(&focusRect, -1, -1);
EntityDark_FrameRect(hDC, focusRect, ENTITY_DARK_BORDER_HOT);
}
else {
RECT frameRect = client;
::InflateRect(&frameRect, -1, -1);
EntityDark_FrameRect(hDC, frameRect, ENTITY_DARK_BORDER);
}
if (oldFont) {
::SelectObject(hDC, oldFont);
}
}
static LRESULT CALLBACK EntityDarkPropertyListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC);
if (!oldProc) {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = ::BeginPaint(hWnd, &ps);
EntityDark_DrawPropertyList(hWnd, hDC);
::EndPaint(hWnd, &ps);
return 0;
}
case WM_PRINTCLIENT:
EntityDark_DrawPropertyList(hWnd, (HDC)wParam);
return 0;
case WM_ERASEBKGND:
{
RECT rc;
::GetClientRect(hWnd, &rc);
EntityDark_FillRect((HDC)wParam, rc, ENTITY_DARK_FIELD);
return 1;
}
case WM_VSCROLL:
case WM_HSCROLL:
case WM_MOUSEWHEEL:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case LB_ADDSTRING:
case LB_INSERTSTRING:
case LB_DELETESTRING:
case LB_RESETCONTENT:
case LB_SETCURSEL:
{
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
if (uMsg == LB_ADDSTRING || uMsg == LB_INSERTSTRING || uMsg == LB_RESETCONTENT) {
EntityDark_SetPropertyListRowHeight(hWnd);
}
::InvalidateRect(hWnd, NULL, FALSE);
return result;
}
case WM_NCDESTROY:
{
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
::RemovePropA(hWnd, ENTITY_DARK_PROP_OLDPROC);
::RemovePropA(hWnd, ENTITY_DARK_PROP_DIVIDER);
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
}
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
static void EntityDark_SubclassPropertyList(CWnd& wnd, int divider) {
HWND hWnd = wnd.GetSafeHwnd();
if (!hWnd) {
return;
}
::SetPropA(hWnd, ENTITY_DARK_PROP_DIVIDER, (HANDLE)(INT_PTR)divider);
EntityDark_SetPropertyListRowHeight(hWnd);
if (::GetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC)) {
::InvalidateRect(hWnd, NULL, TRUE);
return;
}
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
::SetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC, (HANDLE)oldProc);
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkPropertyListProc);
::InvalidateRect(hWnd, NULL, TRUE);
}
CEntityDlg::CEntityDlg(CWnd* pParent /*=NULL*/)
: CDialog(CEntityDlg::IDD, pParent) {
editEntity = NULL;
@@ -37,14 +629,14 @@ CEntityDlg::CEntityDlg(CWnd* pParent /*=NULL*/)
dict = NULL;
themeInitialized = false;
colorBackground = RGB(245, 246, 248);
colorPanel = RGB(255, 255, 255);
colorPanelAlt = RGB(248, 249, 251);
colorBorder = RGB(210, 214, 220);
colorText = RGB(32, 36, 42);
colorTextMuted = RGB(92, 99, 112);
colorAccent = RGB(0, 122, 204);
colorEditBg = RGB(255, 255, 255);
colorBackground = RGB(17, 19, 23);
colorPanel = RGB(26, 29, 34);
colorPanelAlt = RGB(29, 33, 39);
colorBorder = RGB(55, 62, 72);
colorText = RGB(226, 232, 240);
colorTextMuted = RGB(151, 163, 184);
colorAccent = RGB(87, 166, 255);
colorEditBg = RGB(14, 16, 20);
}
CEntityDlg::~CEntityDlg() {
@@ -273,6 +865,30 @@ void CEntityDlg::ApplyModernTheme() {
editVal.SetMargins(6, 6);
}
EntityDark_SubclassField(editKey);
EntityDark_SubclassField(editVal);
EntityDark_SubclassField(listKeyVal);
EntityDark_SubclassField(listVars);
EntityDark_SubclassField(slFrameSlider);
EntityDark_SubclassCombo(comboClass);
EntityDark_SubclassCombo(cbAnimations);
EntityDark_SubclassPropertyList(listKeyVal, 170);
EntityDark_SubclassPropertyList(listVars, 170);
CWnd* darkButtons[] = {
&btnCreate, &btnBrowse,
&btnModel, &btnSound, &btnGui, &btnParticle, &btnSkin, &btnCurve,
&btn135, &btn90, &btn45, &btn180, &btn360, &btn225, &btn270, &btn315, &btnUp, &btnDown,
&btnPlayAnim, &btnStopAnim
};
for (int i = 0; i < (int)(sizeof(darkButtons) / sizeof(darkButtons[0])); i++) {
if (darkButtons[i] && darkButtons[i]->GetSafeHwnd()) {
EntityDark_ApplyNativeTheme(darkButtons[i]->GetSafeHwnd());
EntityDark_SubclassButton(*darkButtons[i]);
}
}
staticTitle.SetWindowText("Entity Inspector");
staticKey.SetWindowText("Key");
staticVal.SetWindowText("Value");
@@ -329,11 +945,17 @@ void CEntityDlg::DrawModernBackground(CDC& dc) {
int contentBottom = rect.bottom - pad;
int width = rect.Width();
if (width >= 560) {
if (width >= 500) {
const int editorH = 106;
const int minToolH = 112;
const int availableH = max(1, contentBottom - contentTop);
// Give the two property grids the extra vertical space instead of capping them at 420px.
// This keeps the edit/tools panels underneath and prevents the list controls from stacking over each other.
int listH = max(180, availableH - editorH - minToolH - pad * 3);
int leftW = (width - pad * 3) / 2;
CRect vars(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + 220);
CRect keyvals(vars.right + pad, contentTop, rect.right - pad, contentTop + 220);
CRect editor(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + 106);
CRect vars(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + listH);
CRect keyvals(vars.right + pad, contentTop, rect.right - pad, contentTop + listH);
CRect editor(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + editorH);
CRect tools(rect.left + pad, editor.bottom + pad, rect.right - pad, contentBottom);
DrawPanel(dc, header, "", true);
@@ -344,11 +966,14 @@ void CEntityDlg::DrawModernBackground(CDC& dc) {
DrawPanel(dc, tools, "Tools", false);
}
else {
int h = max(120, (contentBottom - contentTop - pad * 3) / 4);
const int editorH = 106;
const int minToolH = 112;
const int availableH = max(1, contentBottom - contentTop);
int h = max(140, (availableH - editorH - minToolH - pad * 3) / 2);
CRect vars(rect.left + pad, contentTop, rect.right - pad, contentTop + h);
CRect keyvals(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + h);
CRect editor(rect.left + pad, keyvals.bottom + pad, rect.right - pad, keyvals.bottom + 106);
CRect editor(rect.left + pad, keyvals.bottom + pad, rect.right - pad, keyvals.bottom + editorH);
CRect tools(rect.left + pad, editor.bottom + pad, rect.right - pad, contentBottom);
DrawPanel(dc, header, "", true);
@@ -419,8 +1044,10 @@ BOOL CEntityDlg::OnInitDialog() {
InitModernTheme();
listKeyVal.SetUpdateInspectors(true);
listKeyVal.SetDivider(120);
listVars.SetDivider(120);
listKeyVal.SetDivider(170);
listVars.SetDivider(170);
::SendMessage(listKeyVal.GetSafeHwnd(), LB_SETITEMHEIGHT, 0, 32);
::SendMessage(listVars.GetSafeHwnd(), LB_SETITEMHEIGHT, 0, 32);
staticFrame.SetWindowText("0");
@@ -448,7 +1075,7 @@ void CEntityDlg::OnSize(UINT nType, int cx, int cy) {
const int headerH = 42;
const int classH = 52;
const int editorH = 106;
const int minToolH = 124;
const int minToolH = 112;
CRect rect;
GetClientRect(&rect);
@@ -468,8 +1095,10 @@ void CEntityDlg::OnSize(UINT nType, int cx, int cy) {
int contentTop = classPanel.bottom + pad;
int contentBottom = rect.bottom - pad;
if (w >= 560) {
int listH = max(120, min(260, (h - contentTop - editorH - minToolH - pad * 3)));
if (w >= 500) {
const int availableH = max(1, contentBottom - contentTop);
// No upper cap: the grids now consume the extra dock/inspector height instead of staying squeezed at the top.
int listH = max(180, availableH - editorH - minToolH - pad * 3);
int leftW = (w - pad * 3) / 2;
CRect varsPanel(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + listH);
@@ -534,8 +1163,8 @@ void CEntityDlg::OnSize(UINT nType, int cx, int cy) {
MoveCtrl(staticFrame, animX + animW - 38, toolY + 68, 36, 20);
}
else {
int available = contentBottom - contentTop;
int listH = max(82, (available - editorH - minToolH - pad * 3) / 2);
int available = max(1, contentBottom - contentTop);
int listH = max(140, (available - editorH - minToolH - pad * 3) / 2);
CRect varsPanel(rect.left + pad, contentTop, rect.right - pad, contentTop + listH);
CRect keyPanel(rect.left + pad, varsPanel.bottom + pad, rect.right - pad, varsPanel.bottom + pad + listH);