/* =========================================================================== IceTech GPL Source Code Copyright (C) 2026 Justin Marshall =========================================================================== */ #include "precompiled.h" #pragma hdrstop #include "qe3.h" #include "Radiant.h" #include "GLWidget.h" #include "ConsoleDlg.h" #include "InspectorDialog.h" #include "OutlinerDlg.h" #include "TabsDlg.h" CInspectorDialog* g_Inspectors = NULL; static const COLORREF INSPECTOR_BG = RGB(245, 247, 250); static const COLORREF INSPECTOR_PANEL_BG = RGB(255, 255, 255); static const COLORREF INSPECTOR_TEXT = RGB(32, 36, 42); static const COLORREF INSPECTOR_MUTED_TEXT = RGB(88, 96, 105); static const COLORREF INSPECTOR_BORDER = RGB(218, 223, 230); static const int INSPECTOR_MARGIN = 8; static const int INSPECTOR_TAB_HEIGHT = 42; static const int INSPECTOR_DIVIDER_HEIGHT = 1; void InspectorsDockingCallback(bool docked, int ID, CWnd* wnd) { if (g_Inspectors) { g_Inspectors->SetDockedTabs(docked, ID); } } // CInspectorDialog dialog CInspectorDialog::CInspectorDialog(CWnd* pParent /*=NULL*/) : CTabsDlg(CInspectorDialog::IDD, pParent) { initialized = false; dockedTabs = W_CONSOLE | W_TEXTURE | W_MEDIA | W_OUTLINER; } CInspectorDialog::~CInspectorDialog() { if (modernFont.GetSafeHandle()) { modernFont.DeleteObject(); } if (bgBrush.GetSafeHandle()) { bgBrush.DeleteObject(); } if (editBrush.GetSafeHandle()) { editBrush.DeleteObject(); } if (staticBrush.GetSafeHandle()) { staticBrush.DeleteObject(); } } BEGIN_MESSAGE_MAP(CInspectorDialog, CTabsDlg) ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_INSPECTOR, OnTcnSelchange) ON_WM_SIZE() ON_WM_DESTROY() ON_WM_CLOSE() ON_WM_CTLCOLOR() ON_WM_ERASEBKGND() ON_WM_PAINT() END_MESSAGE_MAP() BOOL CInspectorDialog::OnInitDialog() { CTabsDlg::OnInitDialog(); ASSERT(m_Tabs.GetSafeHwnd()); LoadWindowPlacement(GetSafeHwnd(), "radiant_InspectorsWindow"); consoleWnd.Create(IDD_DIALOG_CONSOLE, this); texWnd.Create(TEXTURE_WINDOW_CLASS, "", QE3_SPLITTER_STYLE, CRect(5, 5, 10, 10), this, 1299); mediaDlg.Create(IDD_DIALOG_TEXTURELIST, this); entityDlg.Create(IDD_DIALOG_ENTITY, this); outlinerDlg.Create(this); dockedTabs = GetCvarInt("radiant_InspectorDockedDialogs", W_CONSOLE | W_TEXTURE | W_MEDIA | W_OUTLINER); AddDockedWindow(&consoleWnd, W_CONSOLE, 1, "Console", (dockedTabs & W_CONSOLE) != 0, InspectorsDockingCallback); AddDockedWindow(&texWnd, W_TEXTURE, 2, "Textures", (dockedTabs & W_TEXTURE) != 0, InspectorsDockingCallback); AddDockedWindow(&mediaDlg, W_MEDIA, 3, "Media", (dockedTabs & W_MEDIA) != 0, InspectorsDockingCallback); AddDockedWindow(&entityDlg, W_ENTITY, 4, "Entity", (dockedTabs & W_ENTITY) != 0, InspectorsDockingCallback); AddDockedWindow(&outlinerDlg, W_OUTLINER, 5, "Outliner", (dockedTabs & W_OUTLINER) != 0, InspectorsDockingCallback); ApplyModernTheme(); SetMode(W_CONSOLE); initialized = true; CRect rc; GetClientRect(&rc); LayoutModern(rc.Width(), rc.Height()); return TRUE; } void CInspectorDialog::ApplyModernTheme() { if (!modernFont.GetSafeHandle()) { LOGFONT lf; memset(&lf, 0, sizeof(lf)); HDC hDC = ::GetDC(GetSafeHwnd()); const int dpiY = GetDeviceCaps(hDC, LOGPIXELSY); ::ReleaseDC(GetSafeHwnd(), hDC); lf.lfHeight = -MulDiv(9, dpiY, 72); lf.lfWeight = FW_NORMAL; lf.lfQuality = CLEARTYPE_QUALITY; strncpy(lf.lfFaceName, "Segoe UI", sizeof(lf.lfFaceName) - 1); lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0'; modernFont.CreateFontIndirect(&lf); } if (!bgBrush.GetSafeHandle()) { bgBrush.CreateSolidBrush(INSPECTOR_BG); } if (!editBrush.GetSafeHandle()) { editBrush.CreateSolidBrush(INSPECTOR_PANEL_BG); } if (!staticBrush.GetSafeHandle()) { staticBrush.CreateSolidBrush(INSPECTOR_BG); } SetFont(&modernFont, FALSE); if (m_Tabs.GetSafeHwnd()) { m_Tabs.SetFont(&modernFont, FALSE); m_Tabs.ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED); } ApplyModernFontRecursive(&consoleWnd); ApplyModernFontRecursive(&mediaDlg); ApplyModernFontRecursive(&entityDlg); ApplyModernFontRecursive(&outlinerDlg); ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED); Invalidate(TRUE); } void CInspectorDialog::ApplyModernFontRecursive(CWnd* wnd) { if (!wnd || !wnd->GetSafeHwnd() || !modernFont.GetSafeHandle()) { return; } wnd->SetFont(&modernFont, FALSE); CWnd* child = wnd->GetWindow(GW_CHILD); while (child) { child->SetFont(&modernFont, FALSE); child = child->GetNextWindow(); } } void CInspectorDialog::SetMode(int mode, bool updateTabs) { FocusWindow(mode); } void CInspectorDialog::UpdateEntitySel(eclass_t* ent) { entityDlg.UpdateEntitySel(ent); } void CInspectorDialog::FillClassList() { entityDlg.AddClassNames(); } void CInspectorDialog::UpdateSelectedEntity() { entityDlg.SetKeyValPairs(); outlinerDlg.RefreshIfNeeded(); } bool CInspectorDialog::GetSelectAllCriteria(idStr& key, idStr& val) { CString k; CString v; entityDlg.editKey.GetWindowText(k); entityDlg.editVal.GetWindowText(v); key = k; val = v; return true; } void CInspectorDialog::LayoutModern(int cx, int cy) { if (!initialized || cx <= 0 || cy <= 0) { return; } const int margin = INSPECTOR_MARGIN; CRect tabRect; tabRect.left = margin; tabRect.right = cx - margin; tabRect.bottom = cy - margin; tabRect.top = tabRect.bottom - INSPECTOR_TAB_HEIGHT; if (tabRect.top < margin) { tabRect.top = margin; } if (m_Tabs.GetSafeHwnd()) { m_Tabs.SetWindowPos( NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_NOZORDER | SWP_NOACTIVATE ); } CRect childRect; childRect.left = margin; childRect.top = margin; childRect.right = cx - margin; childRect.bottom = tabRect.top - margin; if (childRect.right < childRect.left) { childRect.right = childRect.left; } if (childRect.bottom < childRect.top) { childRect.bottom = childRect.top; } DockedWindowInfo* info = NULL; POSITION pos; WORD wID; for (pos = m_Windows.GetStartPosition(); pos != NULL; ) { m_Windows.GetNextAssoc(pos, wID, (void*&)info); if (info && info->m_Window && info->m_Window->GetSafeHwnd() && info->m_State == DockedWindowInfo::DOCKED) { info->m_Window->SetWindowPos( NULL, childRect.left, childRect.top, childRect.Width(), childRect.Height(), SWP_NOZORDER | SWP_NOACTIVATE ); info->m_Window->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED); } } } void CInspectorDialog::OnSize(UINT nType, int cx, int cy) { CTabsDlg::OnSize(nType, cx, cy); if (!initialized) { return; } LayoutModern(cx, cy); Invalidate(FALSE); } BOOL CInspectorDialog::OnEraseBkgnd(CDC* pDC) { CRect rc; GetClientRect(&rc); pDC->FillSolidRect(&rc, INSPECTOR_BG); return TRUE; } void CInspectorDialog::OnPaint() { CPaintDC dc(this); CRect rc; GetClientRect(&rc); dc.FillSolidRect(&rc, INSPECTOR_BG); if (m_Tabs.GetSafeHwnd()) { CRect tabRect; m_Tabs.GetWindowRect(&tabRect); ScreenToClient(&tabRect); CRect lineRect; lineRect.left = INSPECTOR_MARGIN; lineRect.right = rc.Width() - INSPECTOR_MARGIN; lineRect.top = tabRect.top - (INSPECTOR_MARGIN / 2); lineRect.bottom = lineRect.top + INSPECTOR_DIVIDER_HEIGHT; dc.FillSolidRect(&lineRect, INSPECTOR_BORDER); } } HBRUSH CInspectorDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CTabsDlg::OnCtlColor(pDC, pWnd, nCtlColor); if (!pDC) { return hbr; } pDC->SetTextColor(INSPECTOR_TEXT); switch (nCtlColor) { case CTLCOLOR_DLG: pDC->SetBkColor(INSPECTOR_BG); return (HBRUSH)bgBrush.GetSafeHandle(); case CTLCOLOR_STATIC: pDC->SetBkColor(INSPECTOR_BG); pDC->SetTextColor(INSPECTOR_MUTED_TEXT); return (HBRUSH)staticBrush.GetSafeHandle(); case CTLCOLOR_EDIT: case CTLCOLOR_LISTBOX: pDC->SetBkColor(INSPECTOR_PANEL_BG); pDC->SetTextColor(INSPECTOR_TEXT); return (HBRUSH)editBrush.GetSafeHandle(); case CTLCOLOR_BTN: pDC->SetBkColor(INSPECTOR_BG); pDC->SetTextColor(INSPECTOR_TEXT); return (HBRUSH)bgBrush.GetSafeHandle(); default: break; } return hbr; } void CInspectorDialog::OnDestroy() { ::SaveWindowPlacement(GetSafeHwnd(), "radiant_InspectorsWindow"); SetCvarInt("radiant_InspectorDockedDialogs", dockedTabs); CTabsDlg::OnDestroy(); } void CInspectorDialog::OnClose() { CTabsDlg::OnClose(); } BOOL CInspectorDialog::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) { g_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam); } return CTabsDlg::PreTranslateMessage(pMsg); } void CInspectorDialog::SetDockedTabs(bool docked, int ID) { if (docked) { dockedTabs |= ID; } else { dockedTabs &= ~ID; } } void CInspectorDialog::AssignModel() { entityDlg.AssignModel(); }