From 4686349b62c07d3d0b5896498fbd08deb5dac1b9 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Mon, 18 May 2026 15:54:16 -0700 Subject: [PATCH] Added material editor to main tabs. --- .../tools/materialeditor/ConsoleView.cpp | 12 + .../tools/materialeditor/FindDialog.cpp | 4 + .../tools/materialeditor/MEMainFrame.cpp | 470 +++++++++-- .../tools/materialeditor/MaterialDoc.cpp | 2 +- .../tools/materialeditor/MaterialEditView.cpp | 9 + .../tools/materialeditor/MaterialEditor.cpp | 783 +++++++++++++++++- .../MaterialPreviewPropView.cpp | 4 + .../materialeditor/MaterialPreviewView.cpp | 5 +- .../materialeditor/MaterialPropTreeView.cpp | 4 + .../tools/materialeditor/MaterialTreeView.cpp | 13 + neo/engine/tools/materialeditor/StageView.cpp | 3 + .../tools/materialeditor/ToggleListView.cpp | 34 +- neo/engine/tools/radiant/XYWnd.cpp | 79 +- neo/engine/tools/radiant/XYWnd.h | 4 +- 14 files changed, 1300 insertions(+), 126 deletions(-) diff --git a/neo/engine/tools/materialeditor/ConsoleView.cpp b/neo/engine/tools/materialeditor/ConsoleView.cpp index f21a9635..1f1db98d 100644 --- a/neo/engine/tools/materialeditor/ConsoleView.cpp +++ b/neo/engine/tools/materialeditor/ConsoleView.cpp @@ -36,6 +36,10 @@ If you have questions concerning this license or the applicable additional terms #define EDIT_HEIGHT 25 +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); +extern COLORREF MaterialEditorDark_PanelColor(void); +extern COLORREF MaterialEditorDark_TextColor(void); + IMPLEMENT_DYNCREATE(ConsoleView, CFormView) @@ -257,6 +261,14 @@ void ConsoleView::OnInitialUpdate() { if(editInput.m_hWnd) editInput.MoveWindow(0, rect.Height()-EDIT_HEIGHT, rect.Width(), EDIT_HEIGHT); + + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); + if (editConsole.GetSafeHwnd()) { + editConsole.SendMessage(EM_SETBKGNDCOLOR, 0, (LPARAM)MaterialEditorDark_PanelColor()); + } + if (editInput.GetSafeHwnd()) { + editInput.SendMessage(EM_SETBKGNDCOLOR, 0, (LPARAM)MaterialEditorDark_PanelColor()); + } } /** diff --git a/neo/engine/tools/materialeditor/FindDialog.cpp b/neo/engine/tools/materialeditor/FindDialog.cpp index 8f7bb382..61754876 100644 --- a/neo/engine/tools/materialeditor/FindDialog.cpp +++ b/neo/engine/tools/materialeditor/FindDialog.cpp @@ -33,6 +33,8 @@ If you have questions concerning this license or the applicable additional terms #include "MEMainFrame.h" +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); + IMPLEMENT_DYNAMIC(FindDialog, CDialog) BEGIN_MESSAGE_MAP(FindDialog, CDialog) @@ -84,6 +86,8 @@ BOOL FindDialog::OnInitDialog() { LoadFindSettings(); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); + GetDlgItem(IDC_EDIT_FINDTEXT)->SetFocus(); return FALSE; diff --git a/neo/engine/tools/materialeditor/MEMainFrame.cpp b/neo/engine/tools/materialeditor/MEMainFrame.cpp index abb57ac4..0a54e34c 100644 --- a/neo/engine/tools/materialeditor/MEMainFrame.cpp +++ b/neo/engine/tools/materialeditor/MEMainFrame.cpp @@ -38,6 +38,14 @@ If you have questions concerning this license or the applicable additional terms #define TAB_CONTROL 0x1006 +extern bool MaterialEditorIsEmbedded(void); +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); +extern COLORREF MaterialEditorDark_BackgroundColor(void); +extern COLORREF MaterialEditorDark_PanelColor(void); +extern COLORREF MaterialEditorDark_TextColor(void); +extern COLORREF MaterialEditorDark_AccentColor(void); +extern COLORREF MaterialEditorDark_BorderColor(void); + IMPLEMENT_DYNAMIC(MEMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(MEMainFrame, CFrameWnd) @@ -95,6 +103,301 @@ static UINT indicators[] = ID_INDICATOR_SCRL, }; +#define ME_EMBEDDED_MENUBAR_ID 0x6E10 + +static HWND s_meEmbeddedMenuBar = NULL; +static HMENU s_meEmbeddedMenu = NULL; +static CRect s_meEmbeddedMenuRects[64]; +static int s_meEmbeddedMenuRectCount = 0; +static int s_meEmbeddedMenuHotItem = -1; + +static int MEEmbeddedMenuBar_PreferredHeight() { + int h = ::GetSystemMetrics(SM_CYMENU) + 4; + return (h < 22) ? 22 : h; +} + +static COLORREF MEEmbeddedMenuBar_MenuColor() { + return RGB(28, 31, 36); +} + +static COLORREF MEEmbeddedMenuBar_HotColor() { + return RGB(45, 50, 58); +} + + +static void MEEmbeddedMenuBar_FillRect(HDC hDC, const RECT& rc, COLORREF color) { + HBRUSH brush = ::CreateSolidBrush(color); + ::FillRect(hDC, &rc, brush); + ::DeleteObject(brush); +} + +static void MEEmbeddedMenuBar_RebuildRects(HWND hWnd, HDC hDC) { + s_meEmbeddedMenuRectCount = 0; + + if (!s_meEmbeddedMenu) { + return; + } + + int x = 4; + const int y = 1; + const int h = MEEmbeddedMenuBar_PreferredHeight() - 2; + const int count = ::GetMenuItemCount(s_meEmbeddedMenu); + + for (int i = 0; i < count && s_meEmbeddedMenuRectCount < 64; i++) { + char text[128]; + text[0] = '\0'; + ::GetMenuStringA(s_meEmbeddedMenu, i, text, sizeof(text), MF_BYPOSITION); + + SIZE size; + size.cx = 0; + size.cy = 0; + ::GetTextExtentPoint32A(hDC, text, strlen(text), &size); + + CRect rect(x, y, x + size.cx + 22, y + h); + s_meEmbeddedMenuRects[s_meEmbeddedMenuRectCount++] = rect; + x = rect.right + 1; + } +} + +static int MEEmbeddedMenuBar_HitTest(const CPoint& point) { + for (int i = 0; i < s_meEmbeddedMenuRectCount; i++) { + if (s_meEmbeddedMenuRects[i].PtInRect(point)) { + return i; + } + } + return -1; +} + +static void MEEmbeddedMenuBar_Draw(HWND hWnd, HDC hDC) { + RECT client; + ::GetClientRect(hWnd, &client); + MEEmbeddedMenuBar_FillRect(hDC, client, MEEmbeddedMenuBar_MenuColor()); + + HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0); + HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL; + ::SetBkMode(hDC, TRANSPARENT); + + MEEmbeddedMenuBar_RebuildRects(hWnd, hDC); + + if (s_meEmbeddedMenu) { + const int count = ::GetMenuItemCount(s_meEmbeddedMenu); + for (int i = 0; i < count && i < s_meEmbeddedMenuRectCount; i++) { + char text[128]; + text[0] = '\0'; + ::GetMenuStringA(s_meEmbeddedMenu, i, text, sizeof(text), MF_BYPOSITION); + + CRect itemRect = s_meEmbeddedMenuRects[i]; + if (i == s_meEmbeddedMenuHotItem) { + MEEmbeddedMenuBar_FillRect(hDC, itemRect, MEEmbeddedMenuBar_HotColor()); + HPEN pen = ::CreatePen(PS_SOLID, 1, MaterialEditorDark_AccentColor()); + HPEN oldPen = (HPEN)::SelectObject(hDC, pen); + HBRUSH oldBrush = (HBRUSH)::SelectObject(hDC, GetStockObject(NULL_BRUSH)); + ::Rectangle(hDC, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom); + ::SelectObject(hDC, oldBrush); + ::SelectObject(hDC, oldPen); + ::DeleteObject(pen); + } + + ::SetTextColor(hDC, MaterialEditorDark_TextColor()); + ::DrawTextA(hDC, text, -1, &itemRect, DT_SINGLELINE | DT_VCENTER | DT_CENTER); + } + } + + HPEN linePen = ::CreatePen(PS_SOLID, 1, MaterialEditorDark_BorderColor()); + HPEN oldPen = (HPEN)::SelectObject(hDC, linePen); + ::MoveToEx(hDC, client.left, client.bottom - 1, NULL); + ::LineTo(hDC, client.right, client.bottom - 1); + ::SelectObject(hDC, oldPen); + ::DeleteObject(linePen); + + if (oldFont) { + ::SelectObject(hDC, oldFont); + } +} + +static CWnd *MEEmbeddedMenuBar_GetCommandTarget(HWND hWnd) { + HWND hFrame = ::GetParent(hWnd); + if (!hFrame) { + return NULL; + } + + CWnd *target = CWnd::FromHandlePermanent(hFrame); + if (!target) { + target = CWnd::FromHandle(hFrame); + } + return target; +} + +static void MEEmbeddedMenuBar_Track(HWND hWnd, int menuIndex) { + if (!s_meEmbeddedMenu || menuIndex < 0 || menuIndex >= ::GetMenuItemCount(s_meEmbeddedMenu)) { + return; + } + + HMENU hPopup = ::GetSubMenu(s_meEmbeddedMenu, menuIndex); + if (!hPopup) { + return; + } + + CRect itemRect; + if (menuIndex < s_meEmbeddedMenuRectCount) { + itemRect = s_meEmbeddedMenuRects[menuIndex]; + } else { + ::GetClientRect(hWnd, &itemRect); + } + + POINT pt; + pt.x = itemRect.left; + pt.y = itemRect.bottom; + ::ClientToScreen(hWnd, &pt); + + s_meEmbeddedMenuHotItem = menuIndex; + ::InvalidateRect(hWnd, NULL, FALSE); + ::UpdateWindow(hWnd); + + CWnd *target = MEEmbeddedMenuBar_GetCommandTarget(hWnd); + HWND hTarget = target ? target->GetSafeHwnd() : ::GetParent(hWnd); + if (hTarget) { + ::SetForegroundWindow(hTarget); + ::SendMessage(hTarget, WM_INITMENUPOPUP, (WPARAM)hPopup, MAKELPARAM(menuIndex, FALSE)); + } + + UINT command = ::TrackPopupMenu( + hPopup, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, + pt.x, + pt.y, + 0, + hTarget ? hTarget : hWnd, + NULL + ); + + if (command != 0 && hTarget) { + ::SendMessage(hTarget, WM_COMMAND, MAKEWPARAM(command, 0), 0); + } + + s_meEmbeddedMenuHotItem = -1; + ::InvalidateRect(hWnd, NULL, FALSE); + if (hTarget) { + ::PostMessage(hTarget, WM_NULL, 0, 0); + } +} + +static LRESULT CALLBACK MEEmbeddedMenuBar_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_ERASEBKGND: + return 1; + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hDC = ::BeginPaint(hWnd, &ps); + MEEmbeddedMenuBar_Draw(hWnd, hDC); + ::EndPaint(hWnd, &ps); + return 0; + } + case WM_LBUTTONDOWN: + { + HDC hDC = ::GetDC(hWnd); + MEEmbeddedMenuBar_RebuildRects(hWnd, hDC); + ::ReleaseDC(hWnd, hDC); + CPoint pt((short)LOWORD(lParam), (short)HIWORD(lParam)); + MEEmbeddedMenuBar_Track(hWnd, MEEmbeddedMenuBar_HitTest(pt)); + return 0; + } + case WM_MOUSEMOVE: + { + HDC hDC = ::GetDC(hWnd); + MEEmbeddedMenuBar_RebuildRects(hWnd, hDC); + ::ReleaseDC(hWnd, hDC); + CPoint pt((short)LOWORD(lParam), (short)HIWORD(lParam)); + int hot = MEEmbeddedMenuBar_HitTest(pt); + if (hot != s_meEmbeddedMenuHotItem) { + s_meEmbeddedMenuHotItem = hot; + ::InvalidateRect(hWnd, NULL, FALSE); + } + break; + } + case WM_SETFONT: + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + case WM_NCDESTROY: + if (hWnd == s_meEmbeddedMenuBar) { + s_meEmbeddedMenuBar = NULL; + s_meEmbeddedMenuHotItem = -1; + s_meEmbeddedMenuRectCount = 0; + } + break; + } + + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +static BOOL MEEmbeddedMenuBar_Create(CWnd *parent) { + if (!parent || !parent->GetSafeHwnd()) { + return FALSE; + } + + if (s_meEmbeddedMenuBar && ::IsWindow(s_meEmbeddedMenuBar)) { + ::SetParent(s_meEmbeddedMenuBar, parent->GetSafeHwnd()); + ::ShowWindow(s_meEmbeddedMenuBar, SW_SHOW); + return TRUE; + } + + if (!s_meEmbeddedMenu) { + s_meEmbeddedMenu = ::LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ME_MAINFRAME)); + } + + CString className = AfxRegisterWndClass( + CS_HREDRAW | CS_VREDRAW, + ::LoadCursor(NULL, IDC_ARROW), + (HBRUSH)::GetStockObject(BLACK_BRUSH), + NULL + ); + + s_meEmbeddedMenuBar = ::CreateWindowEx( + 0, + className, + _T("MaterialEditorEmbeddedMenuBar"), + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, + 0, + 0, + 0, + MEEmbeddedMenuBar_PreferredHeight(), + parent->GetSafeHwnd(), + (HMENU)ME_EMBEDDED_MENUBAR_ID, + AfxGetInstanceHandle(), + NULL + ); + + if (!s_meEmbeddedMenuBar) { + return FALSE; + } + + ::SetWindowLongPtr(s_meEmbeddedMenuBar, GWLP_WNDPROC, (LONG_PTR)MEEmbeddedMenuBar_WndProc); + if (materialEditorFont && materialEditorFont->GetSafeHandle()) { + ::SendMessage(s_meEmbeddedMenuBar, WM_SETFONT, (WPARAM)materialEditorFont->GetSafeHandle(), TRUE); + } + return TRUE; +} + +static void MEEmbeddedMenuBar_DestroyForParent(HWND hParent) { + if (s_meEmbeddedMenuBar && ::IsWindow(s_meEmbeddedMenuBar) && ::GetParent(s_meEmbeddedMenuBar) == hParent) { + ::DestroyWindow(s_meEmbeddedMenuBar); + s_meEmbeddedMenuBar = NULL; + } + + if (s_meEmbeddedMenu) { + ::DestroyMenu(s_meEmbeddedMenu); + s_meEmbeddedMenu = NULL; + } +} + +static HWND MEEmbeddedMenuBar_GetSafeHwnd() { + return (s_meEmbeddedMenuBar && ::IsWindow(s_meEmbeddedMenuBar)) ? s_meEmbeddedMenuBar : NULL; +} + +static CMenu *MEEmbeddedMenuBar_GetMenu() { + return s_meEmbeddedMenu ? CMenu::FromHandle(s_meEmbeddedMenu) : NULL; +} + /** * Constructor for MEMainFrame. Initialize some member data and load the options. */ @@ -102,6 +405,14 @@ MEMainFrame::MEMainFrame() { currentDoc = NULL; m_find = NULL; + m_consoleView = NULL; + m_materialTreeView = NULL; + m_previewPropertyView = NULL; + m_materialPreviewView = NULL; + m_materialEditView = NULL; + m_stageView = NULL; + m_materialPropertyView = NULL; + m_materialEditSplitter = NULL; searchData.searched = false; @@ -119,7 +430,9 @@ MEMainFrame::~MEMainFrame() { * @param msg The text that is to be added to the console. */ void MEMainFrame::PrintConsoleMessage(const char *msg) { - m_consoleView->AddText(msg); + if (m_consoleView && m_consoleView->GetSafeHwnd()) { + m_consoleView->AddText(msg); + } } /** @@ -130,7 +443,14 @@ BOOL MEMainFrame::PreCreateWindow(CREATESTRUCT& cs) { return FALSE; cs.dwExStyle &= ~WS_EX_CLIENTEDGE; - cs.lpszClass = AfxRegisterWndClass(0); + cs.lpszClass = AfxRegisterWndClass(0, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH)::GetStockObject(BLACK_BRUSH), NULL); + + if (MaterialEditorIsEmbedded()) { + cs.style &= ~(WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); + cs.style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + cs.dwExStyle &= ~(WS_EX_APPWINDOW | WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE); + cs.dwExStyle |= WS_EX_CONTROLPARENT; + } return TRUE; } @@ -140,17 +460,9 @@ BOOL MEMainFrame::PreCreateWindow(CREATESTRUCT& cs) { * creates all of the spliter windows and registers all of the views with the document manager. */ BOOL MEMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { - CCreateContext consoleContext; - consoleContext.m_pNewViewClass = RUNTIME_CLASS(ConsoleView); - - m_consoleView = (ConsoleView*)CreateView(&consoleContext); - m_consoleView->ShowWindow(SW_HIDE); - - m_tabs.Create(TCS_BOTTOM | TCS_FLATBUTTONS | WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, TAB_CONTROL); - m_tabs.InsertItem(0, "Editor"); - m_tabs.InsertItem(1, "Console"); - m_tabs.SetFont(materialEditorFont); - + // The Material Editor now lives inside the Radiant tab host, so the old + // internal Editor/Console tab strip is gone. Console output remains routed + // through Doom's main console instead of a second nested console tab. m_splitterWnd.CreateStatic(this, 2, 1); @@ -249,6 +561,8 @@ BOOL MEMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) SetActiveView(m_materialTreeView); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); + return CFrameWnd::OnCreateClient(lpcs, pContext); } @@ -260,28 +574,20 @@ BOOL MEMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) int MEMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; - - if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || - !m_wndToolBar.LoadToolBar(IDR_ME_MAINFRAME)) - { - TRACE0("Failed to create toolbar\n"); - return -1; // fail to create + // Toolbars and status bars belong to the old standalone Material Editor. + // Keep only a compact menu strip when this frame is embedded in Radiant. + if (MaterialEditorIsEmbedded()) { + MEEmbeddedMenuBar_Create(this); } - + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); - if (!m_wndStatusBar.Create(this) || - !m_wndStatusBar.SetIndicators(indicators, - sizeof(indicators)/sizeof(UINT))) - { - TRACE0("Failed to create status bar\n"); - return -1; // fail to create + //Load the window placement from the options only for the legacy standalone frame. + if (!MaterialEditorIsEmbedded()) { + options.GetWindowPlacement ( "mainframe", m_hWnd ); } - //Load the window placement from the options - options.GetWindowPlacement ( "mainframe", m_hWnd ); - return 0; } @@ -295,33 +601,49 @@ void MEMainFrame::OnDestroy() { int cur; int min; - m_splitterWnd.GetRowInfo(0, cur, min); - options.SetMaterialEditHeight(cur); + if (m_splitterWnd.GetSafeHwnd()) { + m_splitterWnd.GetRowInfo(0, cur, min); + options.SetMaterialEditHeight(cur); + } - m_editSplitter.GetColumnInfo(0, cur, min); - options.SetMaterialTreeWidth(cur); + if (m_editSplitter.GetSafeHwnd()) { + m_editSplitter.GetColumnInfo(0, cur, min); + options.SetMaterialTreeWidth(cur); + } - m_materialEditSplitter->GetColumnInfo(0, cur, min); - options.SetStageWidth(cur); + if (m_materialEditSplitter && m_materialEditSplitter->GetSafeHwnd()) { + m_materialEditSplitter->GetColumnInfo(0, cur, min); + options.SetStageWidth(cur); + } - m_previewSplitter.GetColumnInfo(0, cur, min); - options.SetPreviewPropertiesWidth(cur); + if (m_previewSplitter.GetSafeHwnd()) { + m_previewSplitter.GetColumnInfo(0, cur, min); + options.SetPreviewPropertiesWidth(cur); + } + if (m_materialPropertyView && m_materialPropertyView->GetSafeHwnd()) { + cur = m_materialPropertyView->GetPropertyTreeCtrl().GetColumn(); + options.SetMaterialPropHeadingWidth(cur); + m_materialPropertyView->SaveSettings(); + } - cur = m_materialPropertyView->GetPropertyTreeCtrl().GetColumn(); - options.SetMaterialPropHeadingWidth(cur); + if (m_previewPropertyView && m_previewPropertyView->GetSafeHwnd()) { + cur = m_previewPropertyView->GetPropertyTreeCtrl().GetColumn(); + options.SetPreviewPropHeadingWidth(cur); + } - cur = m_previewPropertyView->GetPropertyTreeCtrl().GetColumn(); - options.SetPreviewPropHeadingWidth(cur); - - options.SetWindowPlacement ( "mainframe", m_hWnd ); + if (!MaterialEditorIsEmbedded()) { + options.SetWindowPlacement ( "mainframe", m_hWnd ); + } options.Save(); - m_materialPropertyView->SaveSettings(); + MEEmbeddedMenuBar_DestroyForParent(GetSafeHwnd()); MaterialDefManager::DestroyMaterialDefLists(); - AfxGetApp()->ExitInstance(); + if (!MaterialEditorIsEmbedded()) { + AfxGetApp()->ExitInstance(); + } } /** @@ -333,22 +655,22 @@ void MEMainFrame::OnSize(UINT nType, int cx, int cy) { CFrameWnd::OnSize(nType, cx, cy); - CRect statusRect; - m_wndStatusBar.GetWindowRect(statusRect); + if (!m_splitterWnd.GetSafeHwnd()) { + return; + } - CRect toolbarRect; - m_wndToolBar.GetWindowRect(toolbarRect); + int menuHeight = 0; + HWND hMenuBar = MEEmbeddedMenuBar_GetSafeHwnd(); + if (MaterialEditorIsEmbedded() && hMenuBar) { + menuHeight = MEEmbeddedMenuBar_PreferredHeight(); + ::MoveWindow(hMenuBar, 0, 0, cx, menuHeight, TRUE); + } - CRect tabRect; - m_tabs.GetItemRect(0, tabRect); - - int tabHeight = tabRect.Height()+5; - - m_splitterWnd.MoveWindow(0, toolbarRect.Height(), cx, cy-statusRect.Height()-toolbarRect.Height()-tabHeight); - - m_tabs.MoveWindow(0, cy-statusRect.Height()-tabHeight, cx, tabHeight); - - m_consoleView->MoveWindow(0, toolbarRect.Height(), cx, cy-statusRect.Height()-toolbarRect.Height()-tabHeight); + int contentHeight = cy - menuHeight; + if (contentHeight < 0) { + contentHeight = 0; + } + m_splitterWnd.MoveWindow(0, menuHeight, cx, contentHeight); } /** @@ -356,23 +678,9 @@ void MEMainFrame::OnSize(UINT nType, int cx, int cy) * the appropriate windows. */ void MEMainFrame::OnTcnSelChange(NMHDR *pNMHDR, LRESULT *pResult) { - - int sel = m_tabs.GetCurSel(); - - switch(sel) { - case 0: - m_splitterWnd.ShowWindow(SW_SHOW); - m_consoleView->ShowWindow(SW_HIDE); - - break; - case 1: - m_splitterWnd.ShowWindow(SW_HIDE); - m_consoleView->ShowWindow(SW_SHOW); - - CRect rect; - GetWindowRect(rect); - MoveWindow(rect); - break; + // The old nested Editor/Console tabs were removed for the integrated layout. + if (pResult) { + *pResult = 0; } } @@ -381,6 +689,10 @@ void MEMainFrame::OnTcnSelChange(NMHDR *pNMHDR, LRESULT *pResult) { * /todo BMatt Nerve: Need to warn the user if a file is modified. */ void MEMainFrame::OnFileExit() { + if (MaterialEditorIsEmbedded()) { + ShowWindow(SW_HIDE); + return; + } PostMessage(WM_DESTROY, 0, 0); } @@ -871,7 +1183,11 @@ void MEMainFrame::OnEditRedoUpdate(CCmdUI *pCmdUI) { */ void MEMainFrame::OnViewIncludeFile() { - CMenu* mmenu = GetMenu(); + CMenu* mmenu = MaterialEditorIsEmbedded() ? MEEmbeddedMenuBar_GetMenu() : GetMenu(); + if (!mmenu) { + m_materialTreeView->InitializeMaterialList(true); + return; + } UINT state = mmenu->GetMenuState(ID_VIEW_INCLUDEFILENAME, MF_BYCOMMAND); ASSERT(state != 0xFFFFFFFF); diff --git a/neo/engine/tools/materialeditor/MaterialDoc.cpp b/neo/engine/tools/materialeditor/MaterialDoc.cpp index af2a3f00..7944d2a3 100644 --- a/neo/engine/tools/materialeditor/MaterialDoc.cpp +++ b/neo/engine/tools/materialeditor/MaterialDoc.cpp @@ -850,7 +850,7 @@ const char* MaterialDoc::GenerateSourceText() { f.WriteFloatString("\n\n/*\n" "\tGenerated by the Material Editor.\n" - "\tType 'materialeditor' at the console to launch the material editor.\n" + "\tGenerated from the embedded Material Editor tab.\n" "*/\n" ); f.WriteFloatString("%s\n", name.c_str()); diff --git a/neo/engine/tools/materialeditor/MaterialEditView.cpp b/neo/engine/tools/materialeditor/MaterialEditView.cpp index 63d7a3f4..2215def8 100644 --- a/neo/engine/tools/materialeditor/MaterialEditView.cpp +++ b/neo/engine/tools/materialeditor/MaterialEditView.cpp @@ -39,6 +39,9 @@ If you have questions concerning this license or the applicable additional terms #define EDIT_TAB_CONTROL 0x2006 #define NAME_CONTROL 0x2007 +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); +extern COLORREF MaterialEditorDark_PanelColor(void); + IMPLEMENT_DYNCREATE(MaterialEditView, CFormView) BEGIN_MESSAGE_MAP(MaterialEditView, CFormView) @@ -192,6 +195,10 @@ void MaterialEditView::OnInitialUpdate() { m_textView.SetText(""); m_textView.SetReadOnly(true); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); + if (m_textView.GetSafeHwnd()) { + m_textView.SendMessage(EM_SETBKGNDCOLOR, 0, (LPARAM)MaterialEditorDark_PanelColor()); + } } } @@ -228,6 +235,8 @@ int MaterialEditView::OnCreate(LPCREATESTRUCT lpCreateStruct) m_tabs.SetFont(materialEditorFont); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); + return 0; } diff --git a/neo/engine/tools/materialeditor/MaterialEditor.cpp b/neo/engine/tools/materialeditor/MaterialEditor.cpp index bfeb966d..8ba3ccb2 100644 --- a/neo/engine/tools/materialeditor/MaterialEditor.cpp +++ b/neo/engine/tools/materialeditor/MaterialEditor.cpp @@ -31,85 +31,795 @@ If you have questions concerning this license or the applicable additional terms #include "../../sys/win32/win_local.h" #include "MaterialEditor.h" + +#ifndef TVM_SETLINECOLOR +#define TVM_SETLINECOLOR (TV_FIRST + 40) +#endif #include "MEMainFrame.h" #ifdef _DEBUG #define new DEBUG_NEW #endif + MEMainFrame* meMainFrame = NULL; CFont* materialEditorFont = NULL; -/** -* Initializes the material editor tool. -*/ -void MaterialEditorInit( void ) { +static bool materialEditorInitialized = false; +static bool materialEditorEmbedded = false; +static CWnd* materialEditorEmbeddedParent = NULL; + +static const COLORREF ME_DARK_BG = RGB(17, 19, 23); +static const COLORREF ME_DARK_PANEL = RGB(23, 26, 31); +static const COLORREF ME_DARK_MENU = RGB(28, 31, 36); +static const COLORREF ME_DARK_MENU_HOT = RGB(45, 50, 58); +static const COLORREF ME_DARK_BORDER = RGB(58, 64, 72); +static const COLORREF ME_DARK_TEXT = RGB(226, 232, 240); +static const COLORREF ME_DARK_TEXT_DISABLED = RGB(101, 110, 126); +static const COLORREF ME_DARK_ACCENT = RGB(87, 166, 255); + +#ifndef BS_TYPEMASK +#define BS_TYPEMASK 0x0000000F +#endif + +#ifndef TCM_GETITEMA +#define TCM_GETITEMA TCM_GETITEM +#endif +#ifndef TCITEMA +#define TCITEMA TCITEM +#endif +#ifndef TCM_INSERTITEMA +#define TCM_INSERTITEMA TCM_INSERTITEM +#endif + +static const char* ME_DARK_BUTTON_OLDPROC = "IceTech.MaterialEditor.DarkButtonOldProc"; +static const char* ME_DARK_STATIC_OLDPROC = "IceTech.MaterialEditor.DarkStaticOldProc"; +static const char* ME_DARK_TAB_OLDPROC = "IceTech.MaterialEditor.DarkTabOldProc"; +static const char* ME_DARK_RECOLOR_OLDPROC = "IceTech.MaterialEditor.DarkRecolorOldProc"; + +bool MaterialEditorOpenDockTab(); + +static HBRUSH MaterialEditorDarkPanelBrush() { + static HBRUSH hBrush = ::CreateSolidBrush(ME_DARK_PANEL); + return hBrush; +} + +static void MaterialEditorDark_FillRect(HDC hDC, const RECT& rc, COLORREF color) { + HBRUSH brush = ::CreateSolidBrush(color); + ::FillRect(hDC, &rc, brush); + ::DeleteObject(brush); +} + +static void MaterialEditorDark_FrameRect(HDC hDC, const RECT& rc, COLORREF color) { + HBRUSH brush = ::CreateSolidBrush(color); + ::FrameRect(hDC, &rc, brush); + ::DeleteObject(brush); +} + +static void MaterialEditorDark_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 MaterialEditorDark_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 ? ME_DARK_TEXT : ME_DARK_TEXT_DISABLED); + + if (isCheck) { + MaterialEditorDark_FillRect(hDC, rc, ME_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; + MaterialEditorDark_FillRect(hDC, box, RGB(14, 16, 20)); + MaterialEditorDark_FrameRect(hDC, box, checked ? ME_DARK_ACCENT : ME_DARK_BORDER); + if (checked) { + HPEN pen = ::CreatePen(PS_SOLID, 2, ME_DARK_ACCENT); + 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; + ::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + } + else { + COLORREF face = pushed ? RGB(22, 25, 30) : RGB(31, 35, 41); + MaterialEditorDark_FillRect(hDC, rc, face); + MaterialEditorDark_FrameRect(hDC, rc, enabled ? ME_DARK_BORDER : RGB(39, 44, 52)); + 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 MaterialEditorDarkButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ME_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); + MaterialEditorDark_DrawButton(hWnd, hDC); + ::EndPaint(hWnd, &ps); + return 0; + } + case WM_PRINTCLIENT: + MaterialEditorDark_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, ME_DARK_BUTTON_OLDPROC); + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + } + } + + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); +} + +static void MaterialEditorDark_SubclassButton(HWND hWnd) { + if (!hWnd || ::GetPropA(hWnd, ME_DARK_BUTTON_OLDPROC)) { + return; + } + WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); + ::SetPropA(hWnd, ME_DARK_BUTTON_OLDPROC, (HANDLE)oldProc); + ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MaterialEditorDarkButtonProc); + ::InvalidateRect(hWnd, NULL, TRUE); +} + +static void MaterialEditorDark_DrawStatic(HWND hWnd, HDC hDC) { + RECT rc; + ::GetClientRect(hWnd, &rc); + MaterialEditorDark_FillRect(hDC, rc, ME_DARK_PANEL); + 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, ME_DARK_TEXT); + ::DrawTextA(hDC, text, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + if (oldFont) { + ::SelectObject(hDC, oldFont); + } +} + +static LRESULT CALLBACK MaterialEditorDarkStaticProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ME_DARK_STATIC_OLDPROC); + if (!oldProc) { + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + } + if (uMsg == WM_PAINT) { + PAINTSTRUCT ps; + HDC hDC = ::BeginPaint(hWnd, &ps); + MaterialEditorDark_DrawStatic(hWnd, hDC); + ::EndPaint(hWnd, &ps); + return 0; + } + if (uMsg == WM_ERASEBKGND) { + return 1; + } + if (uMsg == WM_NCDESTROY) { + ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc); + ::RemovePropA(hWnd, ME_DARK_STATIC_OLDPROC); + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + } + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); +} + +static void MaterialEditorDark_SubclassStatic(HWND hWnd) { + if (!hWnd || ::GetPropA(hWnd, ME_DARK_STATIC_OLDPROC)) { + return; + } + WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); + ::SetPropA(hWnd, ME_DARK_STATIC_OLDPROC, (HANDLE)oldProc); + ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MaterialEditorDarkStaticProc); +} + +static void MaterialEditorDark_DrawTabControl(HWND hWnd, HDC hDC) { + RECT client; + ::GetClientRect(hWnd, &client); + MaterialEditorDark_FillRect(hDC, client, ME_DARK_BG); + + HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0); + HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL; + ::SetBkMode(hDC, TRANSPARENT); + + const int count = (int)::SendMessage(hWnd, TCM_GETITEMCOUNT, 0, 0); + const int sel = (int)::SendMessage(hWnd, TCM_GETCURSEL, 0, 0); + for (int i = 0; i < count; i++) { + RECT item; + if (!::SendMessage(hWnd, TCM_GETITEMRECT, (WPARAM)i, (LPARAM)&item)) { + continue; + } + + const bool active = (i == sel); + RECT fill = item; + fill.bottom += active ? 2 : 0; + MaterialEditorDark_FillRect(hDC, fill, active ? ME_DARK_PANEL : ME_DARK_MENU); + MaterialEditorDark_FrameRect(hDC, fill, active ? ME_DARK_ACCENT : ME_DARK_BORDER); + + char text[128]; + text[0] = '\0'; + TCITEMA ti; + memset(&ti, 0, sizeof(ti)); + ti.mask = TCIF_TEXT; + ti.pszText = text; + ti.cchTextMax = sizeof(text); + ::SendMessageA(hWnd, TCM_GETITEMA, (WPARAM)i, (LPARAM)&ti); + + ::SetTextColor(hDC, active ? RGB(245, 249, 255) : ME_DARK_TEXT); + ::DrawTextA(hDC, text, -1, &item, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); + } + + RECT line = client; + line.top = client.bottom - 1; + MaterialEditorDark_FillRect(hDC, line, ME_DARK_BORDER); + + if (oldFont) { + ::SelectObject(hDC, oldFont); + } +} + +static LRESULT CALLBACK MaterialEditorDarkTabProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ME_DARK_TAB_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); + MaterialEditorDark_DrawTabControl(hWnd, hDC); + ::EndPaint(hWnd, &ps); + return 0; + } + case WM_PRINTCLIENT: + MaterialEditorDark_DrawTabControl(hWnd, (HDC)wParam); + return 0; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case TCM_INSERTITEMA: + case TCM_DELETEITEM: + case TCM_DELETEALLITEMS: + case TCM_SETCURSEL: + { + 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, ME_DARK_TAB_OLDPROC); + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + } + } + + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); +} + +static void MaterialEditorDark_SubclassTab(HWND hWnd) { + if (!hWnd || ::GetPropA(hWnd, ME_DARK_TAB_OLDPROC)) { + return; + } + WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); + ::SetPropA(hWnd, ME_DARK_TAB_OLDPROC, (HANDLE)oldProc); + ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MaterialEditorDarkTabProc); + ::InvalidateRect(hWnd, NULL, TRUE); +} + +static void MaterialEditorDark_SetDIBPixel(BYTE *pixel, COLORREF color) { + pixel[0] = GetBValue(color); + pixel[1] = GetGValue(color); + pixel[2] = GetRValue(color); +} + +static void MaterialEditorDark_RecolorPropTreeWindow(HWND hWnd) { + RECT rc; + ::GetClientRect(hWnd, &rc); + const int width = rc.right - rc.left; + const int height = rc.bottom - rc.top; + if (width <= 0 || height <= 0) { + return; + } + + HDC hDC = ::GetDC(hWnd); + if (!hDC) { + return; + } + + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + void *bits = NULL; + HBITMAP hBitmap = ::CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); + if (!hBitmap || !bits) { + if (hBitmap) { + ::DeleteObject(hBitmap); + } + ::ReleaseDC(hWnd, hDC); + return; + } + + HDC memDC = ::CreateCompatibleDC(hDC); + HBITMAP oldBitmap = (HBITMAP)::SelectObject(memDC, hBitmap); + ::BitBlt(memDC, 0, 0, width, height, hDC, 0, 0, SRCCOPY); + + BYTE *data = (BYTE *)bits; + const int pixelCount = width * height; + for (int i = 0; i < pixelCount; i++) { + BYTE *pixel = data + i * 4; + const int b = pixel[0]; + const int g = pixel[1]; + const int r = pixel[2]; + const int maxColor = max(r, max(g, b)); + const int minColor = min(r, min(g, b)); + + // Only remap neutral system-color pixels. Non-gray pixels such as the + // preview light color swatch are left alone as much as possible. + if (maxColor - minColor <= 18) { + if (maxColor >= 245) { + MaterialEditorDark_SetDIBPixel(pixel, ME_DARK_PANEL); + } else if (maxColor >= 215) { + MaterialEditorDark_SetDIBPixel(pixel, RGB(31, 35, 41)); + } else if (maxColor >= 165) { + MaterialEditorDark_SetDIBPixel(pixel, ME_DARK_MENU_HOT); + } else if (maxColor <= 35) { + MaterialEditorDark_SetDIBPixel(pixel, ME_DARK_TEXT); + } else if (maxColor <= 110) { + MaterialEditorDark_SetDIBPixel(pixel, ME_DARK_TEXT_DISABLED); + } + } else if (b > 130 && g > 70 && r < 120) { + MaterialEditorDark_SetDIBPixel(pixel, ME_DARK_ACCENT); + } + } + + ::BitBlt(hDC, 0, 0, width, height, memDC, 0, 0, SRCCOPY); + ::SelectObject(memDC, oldBitmap); + ::DeleteDC(memDC); + ::DeleteObject(hBitmap); + ::ReleaseDC(hWnd, hDC); +} + +static bool MaterialEditorDark_IsPropTreePart(HWND hWnd) { + if (!hWnd) { + return false; + } + + if (::GetDlgCtrlID(hWnd) == IDC_PROPERTYTREE) { + return true; + } + + HWND parent = ::GetParent(hWnd); + if (parent && ::GetDlgCtrlID(parent) == IDC_PROPERTYTREE) { + return true; + } + + HWND grandParent = parent ? ::GetParent(parent) : NULL; + if (grandParent && ::GetDlgCtrlID(grandParent) == IDC_PROPERTYTREE) { + return true; + } + + return false; +} + +static bool MaterialEditorDark_ShouldRecolorClass(const char *className) { + if (!className || !className[0]) { + return true; + } + if (!_stricmp(className, "Edit") || !_stricmp(className, "RICHEDIT") || !_stricmp(className, "RichEdit20A") || !_stricmp(className, "RichEdit20W")) { + return false; + } + if (!_stricmp(className, "Button") || !_stricmp(className, "Static") || !_stricmp(className, "ComboBox")) { + return false; + } + if (!_stricmp(className, "SysTreeView32") || !_stricmp(className, "SysListView32") || !_stricmp(className, "SysTabControl32")) { + return false; + } + return true; +} + +static LRESULT CALLBACK MaterialEditorDarkRecolorProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ME_DARK_RECOLOR_OLDPROC); + if (!oldProc) { + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + switch (uMsg) { + case WM_ERASEBKGND: + { + RECT rc; + ::GetClientRect(hWnd, &rc); + MaterialEditorDark_FillRect((HDC)wParam, rc, ME_DARK_PANEL); + return 1; + } + case WM_PAINT: + { + LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + MaterialEditorDark_RecolorPropTreeWindow(hWnd); + return result; + } + case WM_PRINTCLIENT: + { + LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + return result; + } + case WM_SIZE: + case WM_ENABLE: + case WM_SETTEXT: + { + LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + ::InvalidateRect(hWnd, NULL, TRUE); + return result; + } + case WM_NCDESTROY: + { + ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc); + ::RemovePropA(hWnd, ME_DARK_RECOLOR_OLDPROC); + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + } + } + + return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); +} + +static void MaterialEditorDark_SubclassRecolor(HWND hWnd) { + if (!hWnd || ::GetPropA(hWnd, ME_DARK_RECOLOR_OLDPROC)) { + return; + } + WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); + ::SetPropA(hWnd, ME_DARK_RECOLOR_OLDPROC, (HANDLE)oldProc); + ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MaterialEditorDarkRecolorProc); + ::InvalidateRect(hWnd, NULL, TRUE); +} + +static void MaterialEditorDark_ApplySingleWindow(HWND hWnd) { + if (!hWnd || !::IsWindow(hWnd)) { + return; + } + + MaterialEditorDark_ApplyNativeTheme(hWnd); + + char className[128]; + className[0] = '\0'; + ::GetClassNameA(hWnd, className, sizeof(className)); + + if (MaterialEditorDark_IsPropTreePart(hWnd) && MaterialEditorDark_ShouldRecolorClass(className)) { + MaterialEditorDark_SubclassRecolor(hWnd); + } + + if (!_stricmp(className, "SysTreeView32")) { + ::SendMessage(hWnd, TVM_SETBKCOLOR, 0, (LPARAM)ME_DARK_PANEL); + ::SendMessage(hWnd, TVM_SETTEXTCOLOR, 0, (LPARAM)ME_DARK_TEXT); + ::SendMessage(hWnd, TVM_SETLINECOLOR, 0, (LPARAM)ME_DARK_BORDER); + } + else if (!_stricmp(className, "SysListView32")) { + ::SendMessage(hWnd, LVM_SETBKCOLOR, 0, (LPARAM)ME_DARK_PANEL); + ::SendMessage(hWnd, LVM_SETTEXTBKCOLOR, 0, (LPARAM)ME_DARK_PANEL); + ::SendMessage(hWnd, LVM_SETTEXTCOLOR, 0, (LPARAM)ME_DARK_TEXT); + ::SendMessage(hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); + } + else if (!_stricmp(className, "SysTabControl32")) { + MaterialEditorDark_SubclassTab(hWnd); + } + else if (!_stricmp(className, "Button")) { + MaterialEditorDark_SubclassButton(hWnd); + } + else if (!_stricmp(className, "Static")) { + MaterialEditorDark_SubclassStatic(hWnd); + } + else if (!_stricmp(className, "Edit") || !_stricmp(className, "RICHEDIT") || !_stricmp(className, "RichEdit20A") || !_stricmp(className, "RichEdit20W")) { + ::SendMessage(hWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)ME_DARK_PANEL); + + CHARFORMAT cf; + memset(&cf, 0, sizeof(cf)); + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_COLOR; + cf.crTextColor = ME_DARK_TEXT; + ::SendMessage(hWnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf); + } + else if (!_stricmp(className, "ComboBox")) { + ::SendMessage(hWnd, CB_SETDROPPEDWIDTH, 260, 0); + } +} + +static BOOL CALLBACK MaterialEditorDark_EnumChildProc(HWND hWnd, LPARAM lParam) { + MaterialEditorDark_ApplySingleWindow(hWnd); + return TRUE; +} + +void MaterialEditorDark_ApplyToWindow(HWND hWnd) { + if (!hWnd || !::IsWindow(hWnd)) { + return; + } + + MaterialEditorDark_ApplySingleWindow(hWnd); + ::EnumChildWindows(hWnd, MaterialEditorDark_EnumChildProc, 0); + ::InvalidateRect(hWnd, NULL, TRUE); +} + +COLORREF MaterialEditorDark_BackgroundColor() { + return ME_DARK_BG; +} + +COLORREF MaterialEditorDark_PanelColor() { + return ME_DARK_PANEL; +} + +COLORREF MaterialEditorDark_TextColor() { + return ME_DARK_TEXT; +} + +COLORREF MaterialEditorDark_AccentColor() { + return ME_DARK_ACCENT; +} + +COLORREF MaterialEditorDark_BorderColor() { + return ME_DARK_BORDER; +} + +static bool MaterialEditorInitCommon( void ) { + if (materialEditorInitialized) { + return true; + } InitPropTree(win32.hInstance); - com_editors = EDITOR_MATERIAL; + com_editors |= EDITOR_MATERIAL; - Sys_GrabMouseCursor( false ); + Sys_GrabMouseCursor(false); InitAfx(); - InitCommonControls(); - // Initialize OLE libraries - if (!AfxOleInit()) - { - return; + // Initialize OLE libraries once for the embedded editor controls. + if (!AfxOleInit()) { + return false; } AfxEnableControlContainer(); NONCLIENTMETRICS info; info.cbSize = sizeof(info); - ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); LOGFONT lf; - memset(&lf, 0, sizeof (LOGFONT)); + memset(&lf, 0, sizeof(LOGFONT)); CWindowDC dc(NULL); lf.lfCharSet = (BYTE)GetTextCharsetInfo(dc.GetSafeHdc(), NULL, 0); - lf.lfHeight = info.lfMenuFont.lfHeight; lf.lfWeight = info.lfMenuFont.lfWeight; lf.lfItalic = info.lfMenuFont.lfItalic; - - // check if we should use system font _tcscpy(lf.lfFaceName, info.lfMenuFont.lfFaceName); materialEditorFont = new CFont; materialEditorFont->CreateFontIndirect(&lf); + materialEditorInitialized = true; + return true; +} - // To create the main window, this code creates a new frame window - // object and then sets it as the application's main window object - meMainFrame = new MEMainFrame; - - // create and load the frame with its resources - meMainFrame->LoadFrame(IDR_ME_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL); +static void MaterialEditorNormalizeEmbeddedFrameStyle( void ) { + if (!meMainFrame || !meMainFrame->GetSafeHwnd()) { + return; + } + HWND hWnd = meMainFrame->GetSafeHwnd(); + LONG style = ::GetWindowLong(hWnd, GWL_STYLE); + style &= ~(WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); + style |= WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + ::SetWindowLong(hWnd, GWL_STYLE, style); - // hide the doom window by default - ::ShowWindow ( win32.hWnd, SW_HIDE ); - - // The one and only window has been initialized, so show and update it + LONG exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE); + exStyle &= ~(WS_EX_APPWINDOW | WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE); + exStyle |= WS_EX_CONTROLPARENT; + ::SetWindowLong(hWnd, GWL_EXSTYLE, exStyle); + + ::SetWindowPos(hWnd, NULL, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); +} + +/** +* Creates or returns the embedded material editor frame hosted by the Radiant tab control. +*/ +HWND MaterialEditorCreateEmbeddedWindow(CWnd *parent, const RECT& rect) { + if (!parent || !parent->GetSafeHwnd()) { + return NULL; + } + + if (!MaterialEditorInitCommon()) { + return NULL; + } + + materialEditorEmbedded = true; + materialEditorEmbeddedParent = parent; + + if (!meMainFrame) { + meMainFrame = new MEMainFrame; + if (!meMainFrame->LoadFrame(IDR_ME_MAINFRAME, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, parent, NULL)) { + delete meMainFrame; + meMainFrame = NULL; + return NULL; + } + } else if (meMainFrame->GetSafeHwnd() && ::GetParent(meMainFrame->GetSafeHwnd()) != parent->GetSafeHwnd()) { + meMainFrame->SetParent(parent); + } + + MaterialEditorNormalizeEmbeddedFrameStyle(); + meMainFrame->MoveWindow(&rect, TRUE); + meMainFrame->ShowWindow(SW_HIDE); + MaterialEditorDark_ApplyToWindow(meMainFrame->GetSafeHwnd()); + return meMainFrame->GetSafeHwnd(); +} + +void MaterialEditorMoveEmbeddedWindow(const RECT& rect) { + if (meMainFrame && meMainFrame->GetSafeHwnd()) { + meMainFrame->MoveWindow(&rect, TRUE); + } +} + +void MaterialEditorShowEmbeddedWindow(BOOL show) { + if (!meMainFrame || !meMainFrame->GetSafeHwnd()) { + return; + } + + meMainFrame->ShowWindow(show ? SW_SHOW : SW_HIDE); + if (show) { + com_editors |= EDITOR_MATERIAL; + MaterialEditorDark_ApplyToWindow(meMainFrame->GetSafeHwnd()); + meMainFrame->SetActiveWindow(); + meMainFrame->SetFocus(); + } else if (materialEditorEmbedded) { + com_editors &= ~EDITOR_MATERIAL; + } +} + +void MaterialEditorFocusEmbeddedWindow( void ) { + if (meMainFrame && meMainFrame->GetSafeHwnd()) { + meMainFrame->SetFocus(); + } +} + +BOOL MaterialEditorPreTranslateEmbeddedMessage(MSG *pMsg) { + if (meMainFrame && meMainFrame->GetSafeHwnd()) { + return meMainFrame->PreTranslateMessage(pMsg); + } + return FALSE; +} + +BOOL MaterialEditorEmbeddedOnCommand(WPARAM wParam, LPARAM lParam) { + if (meMainFrame && meMainFrame->GetSafeHwnd()) { + return (BOOL)meMainFrame->SendMessage(WM_COMMAND, wParam, lParam); + } + return FALSE; +} + +BOOL MaterialEditorEmbeddedOnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult) { + if (meMainFrame && meMainFrame->GetSafeHwnd()) { + LRESULT result = meMainFrame->SendMessage(WM_NOTIFY, wParam, lParam); + if (pResult) { + *pResult = result; + } + return result != 0; + } + return FALSE; +} + +BOOL MaterialEditorEmbeddedOnCmdMsg(UINT nID, int nCode, void *pExtra, AFX_CMDHANDLERINFO *pHandlerInfo) { + if (meMainFrame && meMainFrame->GetSafeHwnd()) { + return meMainFrame->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); + } + return FALSE; +} + +bool MaterialEditorIsEmbedded( void ) { + return materialEditorEmbedded; +} + +/** +* Initializes the material editor tool. In the integrated editor this selects the +* Material Editor tab instead of creating a separate top-level tool window. +*/ +void MaterialEditorInit( void ) { + if (MaterialEditorOpenDockTab()) { + return; + } + + if (!MaterialEditorInitCommon()) { + return; + } + + materialEditorEmbedded = false; + + if (!meMainFrame) { + meMainFrame = new MEMainFrame; + meMainFrame->LoadFrame(IDR_ME_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL); + } + + // Standalone fallback is kept for tools launched before the Radiant tab host exists. meMainFrame->ShowWindow(SW_SHOW); meMainFrame->UpdateWindow(); + MaterialEditorDark_ApplyToWindow(meMainFrame->GetSafeHwnd()); } /** * Called every frame by the doom engine to allow the material editor to process messages. */ void MaterialEditorRun( void ) { - MSG *msg = AfxGetCurrentMessage(); - while( ::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE) ) { - // pump message if ( !AfxGetApp()->PumpMessage() ) { } } @@ -119,27 +829,32 @@ void MaterialEditorRun( void ) { * Called by the doom engine when the material editor needs to be destroyed. */ void MaterialEditorShutdown( void ) { - delete meMainFrame; + meMainFrame = NULL; delete materialEditorFont; + materialEditorFont = NULL; - meMainFrame = NULL; + materialEditorInitialized = false; + materialEditorEmbedded = false; + materialEditorEmbeddedParent = NULL; + com_editors &= ~EDITOR_MATERIAL; } - + /** * Allows the doom engine to reflect console output to the material editors console. */ void MaterialEditorPrintConsole( const char *msg ) { - if(com_editors & EDITOR_MATERIAL) + if ((com_editors & EDITOR_MATERIAL) && meMainFrame) { meMainFrame->PrintConsoleMessage(msg); + } } /** * Returns the handle to the main Material Editor Window */ HWND GetMaterialEditorWindow() { - return meMainFrame->GetSafeHwnd(); + return meMainFrame ? meMainFrame->GetSafeHwnd() : NULL; } /** diff --git a/neo/engine/tools/materialeditor/MaterialPreviewPropView.cpp b/neo/engine/tools/materialeditor/MaterialPreviewPropView.cpp index fd5928cc..19db0277 100644 --- a/neo/engine/tools/materialeditor/MaterialPreviewPropView.cpp +++ b/neo/engine/tools/materialeditor/MaterialPreviewPropView.cpp @@ -31,6 +31,8 @@ If you have questions concerning this license or the applicable additional terms #include "MaterialPreviewPropView.h" +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); + // MaterialPropTreeView @@ -115,6 +117,7 @@ void MaterialPreviewPropView::AddLight( void ) { if ( materialPreview ) { materialPreview->OnAddLight(); } + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); } //Create sample data for the preview properties @@ -223,6 +226,7 @@ void MaterialPreviewPropView::InitializePropTree( void ) { pRoot->SetInfoText(_T("")); AddLight(); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); } // MaterialPreviewPropView drawing diff --git a/neo/engine/tools/materialeditor/MaterialPreviewView.cpp b/neo/engine/tools/materialeditor/MaterialPreviewView.cpp index 91bc344a..c9c32e40 100644 --- a/neo/engine/tools/materialeditor/MaterialPreviewView.cpp +++ b/neo/engine/tools/materialeditor/MaterialPreviewView.cpp @@ -33,6 +33,8 @@ If you have questions concerning this license or the applicable additional terms #include "../radiant/QE3.H" #include "MaterialPreviewView.h" +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); + // MaterialPreviewView IMPLEMENT_DYNCREATE(MaterialPreviewView, CView) @@ -88,6 +90,7 @@ int MaterialPreviewView::OnCreate(LPCREATESTRUCT lpCreateStruct) renderWindow.setDrawable( &renderedView ); renderWindow.SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), SWP_SHOWWINDOW); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); return 0; } @@ -536,7 +539,7 @@ void idGLDrawableView::draw(int x, int y, int w, int h) { drawLights( &refdef ); } - renderSystem->EndFrame( NULL, NULL ); + renderSystem->EndFrame( NULL, NULL, false ); world->DebugClearLines( refdef.time ); diff --git a/neo/engine/tools/materialeditor/MaterialPropTreeView.cpp b/neo/engine/tools/materialeditor/MaterialPropTreeView.cpp index 97ccfbbd..e6a6534d 100644 --- a/neo/engine/tools/materialeditor/MaterialPropTreeView.cpp +++ b/neo/engine/tools/materialeditor/MaterialPropTreeView.cpp @@ -32,6 +32,8 @@ If you have questions concerning this license or the applicable additional terms #define PROP_TREE_VIEW "PropTreeView" +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); + IMPLEMENT_DYNCREATE(MaterialPropTreeView, CPropTreeView) @@ -113,6 +115,7 @@ void MaterialPropTreeView::SetPropertyListType(int listType, int stageNum) { } RefreshProperties(); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); } /** @@ -239,6 +242,7 @@ void MaterialPropTreeView::RefreshProperties() { } } + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); Invalidate(); } diff --git a/neo/engine/tools/materialeditor/MaterialTreeView.cpp b/neo/engine/tools/materialeditor/MaterialTreeView.cpp index 23653132..e05381e7 100644 --- a/neo/engine/tools/materialeditor/MaterialTreeView.cpp +++ b/neo/engine/tools/materialeditor/MaterialTreeView.cpp @@ -30,6 +30,10 @@ If you have questions concerning this license or the applicable additional terms #include "MaterialTreeView.h" +#ifndef TVM_SETLINECOLOR +#define TVM_SETLINECOLOR (TV_FIRST + 40) +#endif + #define IMAGE_FOLDER 0 #define IMAGE_FILE 1 #define IMAGE_MATERIAL 2 @@ -43,6 +47,11 @@ If you have questions concerning this license or the applicable additional terms #define MSG_RENAME_FOLDER_COMPLETE (WM_USER + 1000) #define MSG_RENAME_MATERIAL_COMPLETE (WM_USER + 1001) +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); +extern COLORREF MaterialEditorDark_PanelColor(void); +extern COLORREF MaterialEditorDark_TextColor(void); +extern COLORREF MaterialEditorDark_BorderColor(void); + IMPLEMENT_DYNCREATE(MaterialTreeView, CTreeView) BEGIN_MESSAGE_MAP(MaterialTreeView, CTreeView) @@ -706,6 +715,10 @@ int MaterialTreeView::OnCreate(LPCREATESTRUCT lpCreateStruct) { CTreeCtrl& tree = GetTreeCtrl(); m_image.Create(IDB_ME_TREEBITMAP, 16, 1, RGB(255, 255, 255)); tree.SetImageList(&m_image, TVSIL_NORMAL); + tree.SetBkColor(MaterialEditorDark_PanelColor()); + tree.SetTextColor(MaterialEditorDark_TextColor()); + tree.SendMessage(TVM_SETLINECOLOR, 0, (LPARAM)MaterialEditorDark_BorderColor()); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); return 0; } diff --git a/neo/engine/tools/materialeditor/StageView.cpp b/neo/engine/tools/materialeditor/StageView.cpp index fff5c509..c9bb260a 100644 --- a/neo/engine/tools/materialeditor/StageView.cpp +++ b/neo/engine/tools/materialeditor/StageView.cpp @@ -30,6 +30,8 @@ If you have questions concerning this license or the applicable additional terms #include "StageView.h" +extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); + IMPLEMENT_DYNCREATE(StageView, ToggleListView) BEGIN_MESSAGE_MAP(StageView, ToggleListView) @@ -312,6 +314,7 @@ int StageView::OnCreate(LPCREATESTRUCT lpCreateStruct) { return -1; SetToggleIcons(MAKEINTRESOURCE(IDI_ME_DISABLED_ICON), MAKEINTRESOURCE(IDI_ME_ON_ICON), MAKEINTRESOURCE(IDI_ME_OFF_ICON)); + MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); return 0; } diff --git a/neo/engine/tools/materialeditor/ToggleListView.cpp b/neo/engine/tools/materialeditor/ToggleListView.cpp index f6b4e8ed..3c30acf2 100644 --- a/neo/engine/tools/materialeditor/ToggleListView.cpp +++ b/neo/engine/tools/materialeditor/ToggleListView.cpp @@ -33,6 +33,12 @@ If you have questions concerning this license or the applicable additional terms #define TOGGLELIST_ITEMHEIGHT 22 #define TEXT_OFFSET 6 +extern COLORREF MaterialEditorDark_BackgroundColor(void); +extern COLORREF MaterialEditorDark_PanelColor(void); +extern COLORREF MaterialEditorDark_TextColor(void); +extern COLORREF MaterialEditorDark_AccentColor(void); +extern COLORREF MaterialEditorDark_BorderColor(void); + IMPLEMENT_DYNCREATE(ToggleListView, CListView) @@ -130,6 +136,9 @@ int ToggleListView::OnCreate(LPCREATESTRUCT lpCreateStruct) { CListCtrl& list = GetListCtrl(); list.SetExtendedStyle(LVS_EX_FULLROWSELECT); + list.SetBkColor(MaterialEditorDark_PanelColor()); + list.SetTextBkColor(MaterialEditorDark_PanelColor()); + list.SetTextColor(MaterialEditorDark_TextColor()); //Turn off the horizontal scroll bar //Todo: Figure out why the damn scroll bar pops up @@ -238,12 +247,15 @@ void ToggleListView::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { FrameRect ( lpDrawItemStruct->hDC, &rDraw, (HBRUSH)GetStockObject ( BLACK_BRUSH ) ); rDraw.right --; - FillRect ( lpDrawItemStruct->hDC, &rDraw, GetSysColorBrush ( COLOR_3DFACE ) ); + HBRUSH panelBrush = ::CreateSolidBrush(MaterialEditorDark_PanelColor()); + HBRUSH borderBrush = ::CreateSolidBrush(MaterialEditorDark_BorderColor()); + HBRUSH accentBrush = ::CreateSolidBrush(MaterialEditorDark_AccentColor()); + FillRect ( lpDrawItemStruct->hDC, &rDraw, panelBrush ); - Draw3dRect ( lpDrawItemStruct->hDC, &rDraw, GetSysColorBrush ( COLOR_3DHILIGHT ), GetSysColorBrush ( COLOR_3DSHADOW ) ); + Draw3dRect ( lpDrawItemStruct->hDC, &rDraw, accentBrush, borderBrush ); InflateRect ( &rDraw, -3, -3 ); - Draw3dRect ( lpDrawItemStruct->hDC, &rDraw, GetSysColorBrush ( COLOR_3DSHADOW ), GetSysColorBrush ( COLOR_3DHILIGHT ) ); + Draw3dRect ( lpDrawItemStruct->hDC, &rDraw, borderBrush, accentBrush ); switch(GetToggleState(lvi.iItem)) { case TOGGLE_STATE_DISABLED: @@ -267,18 +279,20 @@ void ToggleListView::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { rDraw.left += TOGGLELIST_ITEMHEIGHT; rDraw.left += 1; - if ( lpDrawItemStruct->itemState & ODS_SELECTED ) { - FillRect ( lpDrawItemStruct->hDC, &rDraw, GetSysColorBrush ( COLOR_HIGHLIGHT ) ); - } else { - FillRect ( lpDrawItemStruct->hDC, &rDraw, GetSysColorBrush ( COLOR_WINDOW ) ); - } + HBRUSH itemBrush = ::CreateSolidBrush((lpDrawItemStruct->itemState & ODS_SELECTED) ? RGB(37, 99, 160) : MaterialEditorDark_PanelColor()); + FillRect ( lpDrawItemStruct->hDC, &rDraw, itemBrush ); + ::DeleteObject(itemBrush); rDraw.left += TEXT_OFFSET; - int colorIndex = ( (lpDrawItemStruct->itemState & ODS_SELECTED ) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ); - SetTextColor ( lpDrawItemStruct->hDC, GetSysColor ( colorIndex ) ); + SetBkMode(lpDrawItemStruct->hDC, TRANSPARENT); + SetTextColor ( lpDrawItemStruct->hDC, MaterialEditorDark_TextColor() ); DrawText ( lpDrawItemStruct->hDC, szBuff, strlen(szBuff), &rDraw, DT_LEFT|DT_VCENTER|DT_SINGLELINE ); + ::DeleteObject(panelBrush); + ::DeleteObject(borderBrush); + ::DeleteObject(accentBrush); + } diff --git a/neo/engine/tools/radiant/XYWnd.cpp b/neo/engine/tools/radiant/XYWnd.cpp index 8a5a566f..3d6fe511 100644 --- a/neo/engine/tools/radiant/XYWnd.cpp +++ b/neo/engine/tools/radiant/XYWnd.cpp @@ -48,6 +48,18 @@ If you have questions concerning this license or the applicable additional terms #define ID_XYDOCK_GAMEPAGE 0x7A04 #define ID_XYDOCK_MODELVIEWPAGE 0x7A05 #define ID_XYDOCK_SCRIPTEDITORPAGE 0x7A06 +#define ID_XYDOCK_MATERIALEDITORPAGE 0x7A07 + +static const int XYDOCK_TAB_MATERIALEDITOR = 4; + +HWND MaterialEditorCreateEmbeddedWindow(CWnd *parent, const RECT& rect); +void MaterialEditorMoveEmbeddedWindow(const RECT& rect); +void MaterialEditorShowEmbeddedWindow(BOOL show); +void MaterialEditorFocusEmbeddedWindow(void); +BOOL MaterialEditorPreTranslateEmbeddedMessage(MSG *pMsg); +BOOL MaterialEditorEmbeddedOnCommand(WPARAM wParam, LPARAM lParam); +BOOL MaterialEditorEmbeddedOnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult); +BOOL MaterialEditorEmbeddedOnCmdMsg(UINT nID, int nCode, void *pExtra, AFX_CMDHANDLERINFO *pHandlerInfo); #define ID_GAME_HOST 0x7A30 #define ID_GAME_FILL_FIT 0x7A40 @@ -1538,6 +1550,8 @@ void CGameDockWnd::OnFill50() { FocusGameWindow(); } +static CXYDockWnd *g_materialEditorDockWnd = NULL; + //============================================================================= // CXYDockWnd //============================================================================= @@ -1555,9 +1569,14 @@ CXYDockWnd::CXYDockWnd() { m_pToolBar = NULL; m_bInLayout = false; m_nActiveTab = XYDOCK_TAB_XY; + g_materialEditorDockWnd = this; } CXYDockWnd::~CXYDockWnd() { + if (g_materialEditorDockWnd == this) { + MaterialEditorShowEmbeddedWindow(FALSE); + g_materialEditorDockWnd = NULL; + } } BOOL CXYDockWnd::Create(CWnd *pParent, UINT nID) { @@ -1613,6 +1632,9 @@ int CXYDockWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { item.pszText = "Script Editor"; m_wndTabs.InsertItem(XYDOCK_TAB_SCRIPTEDITOR, &item); + item.pszText = "Material Editor"; + m_wndTabs.InsertItem(XYDOCK_TAB_MATERIALEDITOR, &item); + CString pageClass = AfxRegisterWndClass( CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW), @@ -1647,6 +1669,10 @@ int CXYDockWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { } m_wndScriptPage.ShowWindow(SW_HIDE); + CRect materialEditorInitialRect(0, 0, 0, 0); + MaterialEditorCreateEmbeddedWindow(&m_wndTabs, materialEditorInitialRect); + MaterialEditorShowEmbeddedWindow(FALSE); + if (!m_wndMenuBar.Create(&m_wndXYPage, 0x7A01)) { return -1; } @@ -1787,6 +1813,7 @@ void CXYDockWnd::ShowActiveTab() { const BOOL gameActive = (m_nActiveTab == XYDOCK_TAB_GAME); const BOOL modelActive = (m_nActiveTab == XYDOCK_TAB_MODELVIEW); const BOOL scriptActive = (m_nActiveTab == XYDOCK_TAB_SCRIPTEDITOR); + const BOOL materialActive = (m_nActiveTab == XYDOCK_TAB_MATERIALEDITOR); if (m_wndXYPage.GetSafeHwnd()) { m_wndXYPage.ShowWindow(xyActive ? SW_SHOW : SW_HIDE); @@ -1822,10 +1849,12 @@ void CXYDockWnd::ShowActiveTab() { } m_wndScriptPage.SetActive(scriptActive); } + + MaterialEditorShowEmbeddedWindow(materialActive); } void CXYDockWnd::SetActiveTab(int nTab) { - if (nTab != XYDOCK_TAB_XY && nTab != XYDOCK_TAB_GAME && nTab != XYDOCK_TAB_MODELVIEW && nTab != XYDOCK_TAB_SCRIPTEDITOR) { + if (nTab != XYDOCK_TAB_XY && nTab != XYDOCK_TAB_GAME && nTab != XYDOCK_TAB_MODELVIEW && nTab != XYDOCK_TAB_SCRIPTEDITOR && nTab != XYDOCK_TAB_MATERIALEDITOR) { nTab = XYDOCK_TAB_XY; } @@ -1900,6 +1929,27 @@ void CXYDockWnd::SetActiveTab(int nTab) { common->ActivateTool(true); FocusScriptEditor(); } + else if (m_nActiveTab == XYDOCK_TAB_MATERIALEDITOR) { + // + // Material Editor tab selected: + // - game HWND is deactivated + // - editor/tool input stays active + // - focus moves to the embedded material editor frame + // + if (m_wndGamePage.GetSafeHwnd()) { + m_wndGamePage.SetActive(FALSE); + } + if (m_wndModelPage.GetSafeHwnd()) { + m_wndModelPage.SetActive(FALSE); + } + if (m_wndScriptPage.GetSafeHwnd()) { + m_wndScriptPage.SetActive(FALSE); + } + + common->ActivateTool(true); + MaterialEditorShowEmbeddedWindow(TRUE); + MaterialEditorFocusEmbeddedWindow(); + } else { // // XY tab selected: @@ -1916,6 +1966,7 @@ void CXYDockWnd::SetActiveTab(int nTab) { if (m_wndScriptPage.GetSafeHwnd()) { m_wndScriptPage.SetActive(FALSE); } + MaterialEditorShowEmbeddedWindow(FALSE); common->ActivateTool(true); FocusXYWindow(); @@ -2002,6 +2053,14 @@ void CXYDockWnd::FocusXYWindow() { m_wndMDIContainer.FocusXYWindow(); } +bool MaterialEditorOpenDockTab() { + if (g_materialEditorDockWnd && g_materialEditorDockWnd->GetSafeHwnd()) { + g_materialEditorDockWnd->SetActiveTab(XYDOCK_TAB_MATERIALEDITOR); + return true; + } + return false; +} + void CXYDockWnd::LayoutChildren() { if (!GetSafeHwnd() || m_bInLayout) { return; @@ -2050,6 +2109,8 @@ void CXYDockWnd::LayoutChildren() { m_wndScriptPage.LayoutChildren(); } + MaterialEditorMoveEmbeddedWindow(pageRect); + ShowActiveTab(); if (m_pToolBar && m_pToolBar->GetSafeHwnd() && m_wndXYPage.GetSafeHwnd()) { @@ -2119,6 +2180,11 @@ BOOL CXYDockWnd::PreTranslateMessage(MSG *pMsg) { return TRUE; } } + if (m_nActiveTab == XYDOCK_TAB_MATERIALEDITOR) { + if (MaterialEditorPreTranslateEmbeddedMessage(pMsg)) { + return TRUE; + } + } return CWnd::PreTranslateMessage(pMsg); } @@ -2144,6 +2210,9 @@ BOOL CXYDockWnd::OnCommand(WPARAM wParam, LPARAM lParam) { m_wndScriptPage.SendMessage(WM_COMMAND, wParam, lParam); return TRUE; } + if (m_nActiveTab == XYDOCK_TAB_MATERIALEDITOR) { + return MaterialEditorEmbeddedOnCommand(wParam, lParam); + } if (m_nActiveTab != XYDOCK_TAB_XY) { return CWnd::OnCommand(wParam, lParam); @@ -2160,6 +2229,11 @@ BOOL CXYDockWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult) { if (wParam == ID_XYDOCK_TABS) { return CWnd::OnNotify(wParam, lParam, pResult); } + if (m_nActiveTab == XYDOCK_TAB_MATERIALEDITOR) { + if (MaterialEditorEmbeddedOnNotify(wParam, lParam, pResult)) { + return TRUE; + } + } CWnd *commandTarget = GetCommandTarget(); if (commandTarget && commandTarget->GetSafeHwnd() && commandTarget != this) { @@ -2183,6 +2257,9 @@ BOOL CXYDockWnd::OnCmdMsg(UINT nID, int nCode, void *pExtra, AFX_CMDHANDLERINFO if (m_nActiveTab == XYDOCK_TAB_SCRIPTEDITOR && m_wndScriptPage.GetSafeHwnd()) { return m_wndScriptPage.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } + if (m_nActiveTab == XYDOCK_TAB_MATERIALEDITOR) { + return MaterialEditorEmbeddedOnCmdMsg(nID, nCode, pExtra, pHandlerInfo); + } if (m_nActiveTab != XYDOCK_TAB_XY) { return FALSE; diff --git a/neo/engine/tools/radiant/XYWnd.h b/neo/engine/tools/radiant/XYWnd.h index e25c7e84..8dc259f8 100644 --- a/neo/engine/tools/radiant/XYWnd.h +++ b/neo/engine/tools/radiant/XYWnd.h @@ -300,11 +300,11 @@ protected: CToolBar *m_pToolBar; bool m_bInLayout; int m_nActiveTab; - +public: void SetActiveTab(int nTab); void ShowActiveTab(); CWnd *GetXYPageParent() const; - +protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg BOOL OnEraseBkgnd(CDC *pDC);