diff --git a/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.cpp b/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.cpp
index 8781486a7b..6ab1039b61 100644
--- a/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.cpp
+++ b/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.cpp
@@ -175,6 +175,12 @@ BEGIN_MESSAGE_MAP(CPropPageFrameDefault, CWnd)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
+//
+bool CPropPageFrameDefault::s_bDarkMode = false;
+COLORREF CPropPageFrameDefault::s_clrFace = RGB(37, 42, 47);
+COLORREF CPropPageFrameDefault::s_clrText = RGB(179, 184, 189);
+//
+
CPropPageFrameDefault::CPropPageFrameDefault()
{
@@ -299,9 +305,22 @@ CRect CPropPageFrameDefault::CalcCaptionArea()
void CPropPageFrameDefault::DrawCaption(CDC *pDc, CRect rect, LPCTSTR lpszCaption, HICON hIcon)
{
//
- COLORREF clrLeft = GetSysColor(COLOR_ACTIVECAPTION);
+ auto Lighten = [](COLORREF c, int d) -> COLORREF {
+ int r = GetRValue(c) + d; if (r > 255) { r = 255; }
+ int g = GetGValue(c) + d; if (g > 255) { g = 255; }
+ int b = GetBValue(c) + d; if (b > 255) { b = 255; }
+ return RGB(r, g, b);
+ };
+
+ COLORREF clrLeft;
+ COLORREF clrRight;
+ if (s_bDarkMode) {
+ clrLeft = clrRight = Lighten(s_clrFace, 14); // subtle header band above the page
+ } else {
+ clrLeft = GetSysColor(COLOR_ACTIVECAPTION);
+ clrRight = pDc->GetPixel(rect.right-1, rect.top);
+ }
//
- COLORREF clrRight = pDc->GetPixel(rect.right-1, rect.top);
FillGradientRectH(pDc, rect, clrLeft, clrRight);
// draw icon
@@ -317,7 +336,7 @@ void CPropPageFrameDefault::DrawCaption(CDC *pDc, CRect rect, LPCTSTR lpszCaptio
// draw text
rect.left += 2;
- COLORREF clrPrev = pDc->SetTextColor(GetSysColor(COLOR_CAPTIONTEXT));
+ COLORREF clrPrev = pDc->SetTextColor(s_bDarkMode ? s_clrText : GetSysColor(COLOR_CAPTIONTEXT));
int nBkStyle = pDc->SetBkMode(TRANSPARENT);
CFont *pFont = (CFont*)pDc->SelectStockObject(SYSTEM_FONT);
@@ -405,6 +424,15 @@ void CPropPageFrameDefault::OnPaint()
BOOL CPropPageFrameDefault::OnEraseBkgnd(CDC* pDC)
{
+ //
+ if (s_bDarkMode) {
+ CRect rect;
+ GetClientRect(rect);
+ pDC->FillSolidRect(rect, s_clrFace);
+ return TRUE;
+ }
+ //
+
if (g_ThemeLib.IsAvailable() && g_ThemeLib.IsThemeActive())
{
HTHEME hTheme = g_ThemeLib.OpenThemeData(m_hWnd, L"Tab");
diff --git a/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.h b/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.h
index 8063a29baf..b651bdeb25 100644
--- a/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.h
+++ b/src/ExtLib/ui/TreePropSheet/PropPageFrameDefault.h
@@ -46,6 +46,15 @@ class /*AFX_EXT_CLASS*/ CPropPageFrameDefault : public CWnd,
CPropPageFrameDefault();
virtual ~CPropPageFrameDefault();
+//
+// Dark theme palette, set by the host (CPPageSheet) before the frame paints.
+// When s_bDarkMode is false the classic (system-colored) appearance is used.
+public:
+ static bool s_bDarkMode;
+ static COLORREF s_clrFace;
+ static COLORREF s_clrText;
+//
+
// operations
public:
diff --git a/src/ExtLib/ui/coolsb/coolsblib.c b/src/ExtLib/ui/coolsb/coolsblib.c
index 2d0cb736ad..0d4e11a983 100644
--- a/src/ExtLib/ui/coolsb/coolsblib.c
+++ b/src/ExtLib/ui/coolsb/coolsblib.c
@@ -37,6 +37,10 @@
static TCHAR szPropStr[] = _T("CoolSBSubclassPtr");
+// Single definition of the theme-colour callback (declared extern in coolscroll.h so the
+// header can be included from more than one translation unit without a duplicate symbol).
+ptr_themeRGB fThemeRGB = NULL;
+
LRESULT CALLBACK CoolSBWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
SCROLLWND *GetScrollWndFromHwnd(HWND hwnd)
diff --git a/src/ExtLib/ui/coolsb/coolscroll.h b/src/ExtLib/ui/coolsb/coolscroll.h
index 682b0f2804..2c1b82994f 100644
--- a/src/ExtLib/ui/coolsb/coolscroll.h
+++ b/src/ExtLib/ui/coolsb/coolscroll.h
@@ -143,7 +143,7 @@ int WINAPI CoolSB_SetScrollPos (HWND hwnd, int nBar, int nPos, BOOL fRedraw);
int WINAPI CoolSB_SetScrollRange (HWND hwnd, int nBar, int nMinPos, int nMaxPos, BOOL fRedraw);
BOOL WINAPI CoolSB_ShowScrollBar (HWND hwnd, int wBar, BOOL fShow);
-ptr_themeRGB fThemeRGB;
+extern ptr_themeRGB fThemeRGB;
//
// Scrollbar dimension functions
diff --git a/src/ExtLib/ui/sizecbar/scbarg.cpp b/src/ExtLib/ui/sizecbar/scbarg.cpp
index 7e8668cfc0..5cbf04e9fe 100644
--- a/src/ExtLib/ui/sizecbar/scbarg.cpp
+++ b/src/ExtLib/ui/sizecbar/scbarg.cpp
@@ -116,36 +116,36 @@ void CSizingControlBarG::NcPaintGripper(CDC* pDC, CRect rcClient)
if (!HasGripper())
return;
- if (!m_bUseDarkTheme) {
- // paints a simple "two raised lines" gripper
- // override this if you want a more sophisticated gripper
- CRect gripper = rcClient;
- CRect rcbtn = m_biHide.GetRect(CSize(ScaleX(m_cyGripper), ScaleY(m_cyGripper)));
- BOOL bHorz = IsHorzDocked();
-
- gripper.DeflateRect(1, 1);
- const auto sizeX = ScaleX(m_cyGripper) / 4;
- const auto sizeY = ScaleY(m_cyGripper) / 4;
- if (bHorz)
- { // gripper at left
- gripper.left -= (ScaleX(m_cyGripper) / 2 + sizeX * 2);
- gripper.right = gripper.left + sizeX;
- gripper.top = rcbtn.bottom + 3;
- }
- else
- { // gripper at top
- gripper.top -= (ScaleY(m_cyGripper) / 2 + sizeY * 2);
- gripper.bottom = gripper.top + sizeY;
- gripper.right = rcbtn.left - 3;
- }
- pDC->Draw3dRect(gripper, ::GetSysColor(COLOR_BTNHIGHLIGHT),
- ::GetSysColor(COLOR_BTNSHADOW));
+ // paints a simple "two raised lines" gripper
+ // override this if you want a more sophisticated gripper
+ CRect gripper = rcClient;
+ CRect rcbtn = m_biHide.GetRect(CSize(ScaleX(m_cyGripper), ScaleY(m_cyGripper)));
+ BOOL bHorz = IsHorzDocked();
+
+ gripper.DeflateRect(1, 1);
+ const auto sizeX = ScaleX(m_cyGripper) / 4;
+ const auto sizeY = ScaleY(m_cyGripper) / 4;
+ if (bHorz)
+ { // gripper at left
+ gripper.left -= (ScaleX(m_cyGripper) / 2 + sizeX * 2);
+ gripper.right = gripper.left + sizeX;
+ gripper.top = rcbtn.bottom + 3;
+ }
+ else
+ { // gripper at top
+ gripper.top -= (ScaleY(m_cyGripper) / 2 + sizeY * 2);
+ gripper.bottom = gripper.top + sizeY;
+ gripper.right = rcbtn.left - 3;
+ }
- gripper.OffsetRect(bHorz ? sizeX : 0, bHorz ? 0 : sizeY);
+ // In the dark theme the system 3D colours are near-black on the dark frame (the gripper lines
+ // vanished); use themed light/dark greys so the two raised lines stay visible.
+ const COLORREF clrHi = m_bUseDarkTheme ? ColorThemeRGB(95, 100, 105) : ::GetSysColor(COLOR_BTNHIGHLIGHT);
+ const COLORREF clrLo = m_bUseDarkTheme ? ColorThemeRGB(20, 25, 30) : ::GetSysColor(COLOR_BTNSHADOW);
- pDC->Draw3dRect(gripper, ::GetSysColor(COLOR_BTNHIGHLIGHT),
- ::GetSysColor(COLOR_BTNSHADOW));
- }
+ pDC->Draw3dRect(gripper, clrHi, clrLo);
+ gripper.OffsetRect(bHorz ? sizeX : 0, bHorz ? 0 : sizeY);
+ pDC->Draw3dRect(gripper, clrHi, clrLo);
m_biHide.Paint(pDC, this, CSize(ScaleX(m_cyGripper), ScaleY(m_cyGripper)));
}
diff --git a/src/ExtLib/ui/sizecbar/sizecbar.cpp b/src/ExtLib/ui/sizecbar/sizecbar.cpp
index 210125ca4f..12212b5cb8 100644
--- a/src/ExtLib/ui/sizecbar/sizecbar.cpp
+++ b/src/ExtLib/ui/sizecbar/sizecbar.cpp
@@ -82,6 +82,7 @@ CSizingControlBar::CSizingControlBar()
m_hBrush_orig = nullptr;
m_hBrush = nullptr;
+ m_hBrushClient = nullptr;
m_dwBrushColor = 0;
m_bUseDarkTheme = false;
@@ -90,13 +91,15 @@ CSizingControlBar::CSizingControlBar()
CSizingControlBar::~CSizingControlBar()
{
+ // (m_hBrush was deleted twice here and m_hBrushFrame was guarded by m_hBrush_orig, so the frame brush
+ // leaked while a freed handle was passed to DeleteObject a second time. Each brush is freed once now.)
if (m_hBrush) {
::DeleteObject(m_hBrush);
}
- if (m_hBrush_orig) {
- ::DeleteObject(m_hBrush);
+ if (m_hBrushClient) {
+ ::DeleteObject(m_hBrushClient);
}
- if (m_hBrush_orig) {
+ if (m_hBrushFrame) {
::DeleteObject(m_hBrushFrame);
}
}
@@ -106,6 +109,7 @@ BEGIN_MESSAGE_MAP(CSizingControlBar, baseCSizingControlBar)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_NCPAINT()
+ ON_WM_ERASEBKGND()
ON_WM_NCCALCSIZE()
ON_WM_WINDOWPOSCHANGING()
ON_WM_CAPTURECHANGED()
@@ -573,15 +577,30 @@ void CSizingControlBar::OnNcPaint()
//MPC-BE custom code start
if (m_bUseDarkTheme) {
const auto dwBrushColor = ColorThemeRGB(45, 50, 55);
- if (m_dwBrushColor != dwBrushColor && m_hBrush) {
- ::DeleteObject(m_hBrush);
- m_hBrush = nullptr;
+ if (m_dwBrushColor != dwBrushColor) {
+ // the theme sliders moved - drop both cached brushes so they re-tint
+ if (m_hBrush) {
+ ::DeleteObject(m_hBrush);
+ m_hBrush = nullptr;
+ }
+ if (m_hBrushClient) {
+ ::DeleteObject(m_hBrushClient);
+ m_hBrushClient = nullptr;
+ }
}
m_dwBrushColor = dwBrushColor;
if (!m_hBrush) {
m_hBrush = ::CreateSolidBrush(dwBrushColor);
}
- ::SetClassLongPtrW(m_hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)m_hBrush); //MPC-BE patch
+ // The CLASS background brush is what DefWindowProc uses to erase a client strip that gets exposed
+ // during a redock/resize (before the child dialog catches up). It must be the DARK CONTENT shade,
+ // not the lighter NC/caption shade above: ColorThemeRGB scales with the brightness slider, so
+ // (45,50,55) reads as a pale/near-white rectangle at high brightness while the hosted content
+ // (shader editor dialog, lists) is (22,27,32). Keep the NC frame fill below on m_hBrush.
+ if (!m_hBrushClient) {
+ m_hBrushClient = ::CreateSolidBrush(ColorThemeRGB(22, 27, 32));
+ }
+ ::SetClassLongPtrW(m_hWnd, GCLP_HBRBACKGROUND, (LONG_PTR)m_hBrushClient); //MPC-BE patch
mdc.FrameRect(rcDraw, CBrush::FromHandle(m_hBrushFrame)); // Draw Black Frame
@@ -631,6 +650,25 @@ void CSizingControlBar::OnPaint()
CPaintDC dc(this);
}
+BOOL CSizingControlBar::OnEraseBkgnd(CDC* pDC)
+{
+ // The bar's own client background is otherwise erased by DefWindowProc with the shared window-class
+ // brush (light COLOR_BTNFACE), which is only swapped dark as a side effect of the dark NC paint - so
+ // when a child doesn't fully cover the client (e.g. a 2px inset), or right after a redock/resize/theme
+ // toggle before the next NC paint, the exposed strip flashed WHITE. Paint it dark deterministically.
+ if (m_bUseDarkTheme) {
+ CRect rc;
+ GetClientRect(rc);
+ // Match the DARK content the bar hosts (22,27,32), NOT the lighter NC/caption shade: ColorThemeRGB
+ // scales with the brightness slider, so the old (45,50,55) fill showed up as a pale/near-white
+ // rectangle whenever a strip was briefly exposed on redock/resize.
+ pDC->FillSolidRect(rc, ColorThemeRGB(22, 27, 32));
+ return TRUE;
+ }
+
+ return baseCSizingControlBar::OnEraseBkgnd(pDC);
+}
+
LRESULT CSizingControlBar::OnNcHitTest(CPoint point)
{
CRect rcBar, rcEdge;
diff --git a/src/ExtLib/ui/sizecbar/sizecbar.h b/src/ExtLib/ui/sizecbar/sizecbar.h
index 34ad882f7e..74a1c3753a 100644
--- a/src/ExtLib/ui/sizecbar/sizecbar.h
+++ b/src/ExtLib/ui/sizecbar/sizecbar.h
@@ -172,6 +172,9 @@ class CSizingControlBar : public baseCSizingControlBar
CSize m_szFixedFloat;
HBRUSH m_hBrush, m_hBrush_orig, m_hBrushFrame;
+ // Separate CLIENT background brush. m_hBrush is the lighter NC/caption shade; the client must match the
+ // DARK content the bar hosts (shader editor dialog / lists), or an exposed strip reads as a pale rectangle.
+ HBRUSH m_hBrushClient;
COLORREF m_dwBrushColor;
//MPC-BE custom code end
@@ -195,6 +198,7 @@ class CSizingControlBar : public baseCSizingControlBar
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnWindowPosChanging(WINDOWPOS FAR* lpwndpos);
afx_msg void OnPaint();
+ afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnClose();
afx_msg void OnSize(UINT nType, int cx, int cy);
//}}AFX_MSG
diff --git a/src/apps/mplayerc/AboutDlg.cpp b/src/apps/mplayerc/AboutDlg.cpp
index 06988f4c31..ec4f5d065a 100644
--- a/src/apps/mplayerc/AboutDlg.cpp
+++ b/src/apps/mplayerc/AboutDlg.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "AboutDlg.h"
#include "DSUtil/FileHandle.h"
+#include "controls/DarkTheme.h"
#include "Version.h"
@@ -94,6 +95,8 @@ BOOL CAboutDlg::OnInitDialog()
GetDlgItem(IDOK)->SetFocus();
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return FALSE;
}
diff --git a/src/apps/mplayerc/AddCommandDlg.cpp b/src/apps/mplayerc/AddCommandDlg.cpp
index f62184feed..0aeb3cdb95 100644
--- a/src/apps/mplayerc/AddCommandDlg.cpp
+++ b/src/apps/mplayerc/AddCommandDlg.cpp
@@ -20,6 +20,7 @@
#include "stdafx.h"
#include "AddCommandDlg.h"
+#include "controls/DarkTheme.h"
// CAddCommandDlg dialog
@@ -137,6 +138,8 @@ BOOL CAddCommandDlg::OnInitDialog()
SHAutoComplete(m_FilterEdit, SHACF_AUTOAPPEND_FORCE_OFF | SHACF_AUTOSUGGEST_FORCE_OFF);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/AppSettings.cpp b/src/apps/mplayerc/AppSettings.cpp
index 3d9fccd25c..43a5608304 100644
--- a/src/apps/mplayerc/AppSettings.cpp
+++ b/src/apps/mplayerc/AppSettings.cpp
@@ -852,6 +852,11 @@ void CAppSettings::LoadSettings(bool bForce/* = false*/)
if (iLanguage < 0) {
iLanguage = CMPlayerCApp::GetDefLanguage();
}
+ // Read the dark-theme flag before SetLanguage: SetLanguage may pop the "language pack will not
+ // work with this version" message box, and DarkTheme::IsActive() must already reflect the user's
+ // saved preference for that box to be themed (bUseDarkTheme is otherwise not read until further
+ // below, so at this point it would still hold the ResetSettings default). Harmless duplicate read.
+ profile.ReadBool(IDS_R_THEME, IDS_RS_USEDARKTHEME, bUseDarkTheme);
CMPlayerCApp::SetLanguage(iLanguage, false);
FiltersPriority.LoadSettings();
diff --git a/src/apps/mplayerc/AuthDlg.cpp b/src/apps/mplayerc/AuthDlg.cpp
index 7c5220e2b4..0b25290a22 100644
--- a/src/apps/mplayerc/AuthDlg.cpp
+++ b/src/apps/mplayerc/AuthDlg.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "AuthDlg.h"
+#include "controls/DarkTheme.h"
// CAuthDlg dialog
@@ -82,6 +83,8 @@ BOOL CAuthDlg::OnInitDialog()
m_usernamectrl.SetFocus();
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/CmdLineHelpDlg.cpp b/src/apps/mplayerc/CmdLineHelpDlg.cpp
index b44047df88..2cbff018e0 100644
--- a/src/apps/mplayerc/CmdLineHelpDlg.cpp
+++ b/src/apps/mplayerc/CmdLineHelpDlg.cpp
@@ -22,6 +22,7 @@
#include "stdafx.h"
#include "CmdLineHelpDlg.h"
#include "Misc.h"
+#include "controls/DarkTheme.h"
CmdLineHelpDlg::CmdLineHelpDlg(const CStringW& cmdLine)
: CResizableDialog(CmdLineHelpDlg::IDD)
@@ -117,5 +118,7 @@ BOOL CmdLineHelpDlg::OnInitDialog()
EnableSaveRestore(IDS_R_DLG_CMD_LINE_HELP);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return FALSE;
}
diff --git a/src/apps/mplayerc/ComPropertySheet.cpp b/src/apps/mplayerc/ComPropertySheet.cpp
index cb07058ba8..be7c73be5d 100644
--- a/src/apps/mplayerc/ComPropertySheet.cpp
+++ b/src/apps/mplayerc/ComPropertySheet.cpp
@@ -22,6 +22,11 @@
#include "stdafx.h"
#include "ComPropertySheet.h"
#include "filters/filters/InternalPropertyPage.h"
+#include "controls/DarkTheme.h"
+
+// windowsx.h defines a function-like SubclassWindow(hwnd, lpfn) macro that collides with
+// CWnd::SubclassWindow(HWND); undef it so the MFC method call below parses correctly.
+#undef SubclassWindow
// CComPropertyPageSite
@@ -228,6 +233,14 @@ void CComPropertySheet::OnActivated(CPropertyPage* pPage)
MoveWindow(CRect(wr.TopLeft(), ws + diff));
+ // Dark-theme this page: its host dialog (pChild) gets its own WM_CTLCOLOR*, so the sheet's
+ // subclass doesn't reach it — theme it directly (dark background/text + themed controls).
+ if (DarkTheme::IsActive()) {
+ if (CWnd* pChild = pPage->GetWindow(GW_CHILD)) {
+ DarkTheme::ThemeDialog(pChild->GetSafeHwnd());
+ }
+ }
+
Invalidate();
}
@@ -244,5 +257,18 @@ BOOL CComPropertySheet::OnInitDialog()
CenterWindow();
}
+ // The stock tab control ignores SetWindowTheme and keeps a light body with a ~3px light frame;
+ // attach the owner-drawn dark tab so it matches (it falls back to native when the theme is off).
+ if (DarkTheme::IsActive()) {
+ if (CWnd* pTab = GetDlgItem(AFX_IDC_TAB_CONTROL)) {
+ m_dark_tab.SubclassWindow(pTab->GetSafeHwnd());
+ }
+ }
+
+ // Dark-theme the whole filter-config sheet (frame, tab, OK/Cancel/Apply) so it matches the
+ // Options dialog it was opened from. Each page's own background/controls are themed as the
+ // page is activated (OnActivated), since a property page dialog gets its WM_CTLCOLOR* itself.
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return bResult;
}
diff --git a/src/apps/mplayerc/ComPropertySheet.h b/src/apps/mplayerc/ComPropertySheet.h
index 47a960a8b1..bd823354d7 100644
--- a/src/apps/mplayerc/ComPropertySheet.h
+++ b/src/apps/mplayerc/ComPropertySheet.h
@@ -22,6 +22,7 @@
#pragma once
#include "ComPropertyPage.h"
+#include "controls/DarkTabCtrl.h"
interface IComPropertyPageDirty {
@@ -38,6 +39,7 @@ class CComPropertySheet : public CPropertySheet, public IComPropertyPageDirty
std::list> m_spp;
std::list m_pages;
CSize m_size;
+ CDarkTabCtrl m_dark_tab; // owner-drawn dark tab, attached in OnInitDialog when the dark theme is on
public:
CComPropertySheet(UINT nIDCaption, CWnd* pParentWnd = nullptr, UINT iSelectPage = 0);
diff --git a/src/apps/mplayerc/FavoriteAddDlg.cpp b/src/apps/mplayerc/FavoriteAddDlg.cpp
index c519300fb4..826dd90b3c 100644
--- a/src/apps/mplayerc/FavoriteAddDlg.cpp
+++ b/src/apps/mplayerc/FavoriteAddDlg.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "FavoriteAddDlg.h"
+#include "controls/DarkTheme.h"
// CFavoriteAddDlg dialog
@@ -74,6 +75,8 @@ BOOL CFavoriteAddDlg::OnInitDialog()
m_namectrl.SetCurSel(0);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/FavoriteOrganizeDlg.cpp b/src/apps/mplayerc/FavoriteOrganizeDlg.cpp
index 8819728b59..debc96df6f 100644
--- a/src/apps/mplayerc/FavoriteOrganizeDlg.cpp
+++ b/src/apps/mplayerc/FavoriteOrganizeDlg.cpp
@@ -24,6 +24,7 @@
#include "ItemPropertiesDlg.h"
#include "FavoriteOrganizeDlg.h"
#include "DSUtil/std_helper.h"
+#include "controls/DarkTheme.h"
// CFavoriteOrganizeDlg dialog
@@ -178,6 +179,8 @@ BOOL CFavoriteOrganizeDlg::OnInitDialog()
EnableSaveRestore(IDS_R_DLG_ORGANIZE_FAV);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
@@ -211,20 +214,29 @@ void CFavoriteOrganizeDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStr
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
+ // This list is owner-drawn, so ThemeDialog can't recolour it — paint the rows in the dark palette
+ // ourselves (the fixed light COLOR_WINDOW / black text left the items white on the dark theme).
+ const bool dark = DarkTheme::IsActive();
+
CBrush b;
if (!!m_list.GetItemState(nItem, LVIS_SELECTED)) {
- b.CreateSolidBrush(0xf1dacc);
+ b.CreateSolidBrush(dark ? RGB(38, 79, 120) : 0xf1dacc);
pDC->FillRect(rcItem, &b);
b.DeleteObject();
- b.CreateSolidBrush(0xc56a31);
+ b.CreateSolidBrush(dark ? RGB(76, 194, 255) : 0xc56a31);
pDC->FrameRect(rcItem, &b);
} else {
- b.CreateSysColorBrush(COLOR_WINDOW);
+ if (dark) {
+ b.CreateSolidBrush(DarkTheme::FaceColor());
+ } else {
+ b.CreateSysColorBrush(COLOR_WINDOW);
+ }
pDC->FillRect(rcItem, &b);
}
CStringW str;
- pDC->SetTextColor(0);
+ pDC->SetBkMode(TRANSPARENT); // don't paint a light box behind the text over the dark row
+ pDC->SetTextColor(dark ? DarkTheme::TextColor() : 0);
str = m_list.GetItemText(nItem, 0);
pDC->TextOut(rcItem.left + 3, (rcItem.top + rcItem.bottom - pDC->GetTextExtent(str).cy) / 2, str);
diff --git a/src/apps/mplayerc/FavoriteOrganizeDlg.h b/src/apps/mplayerc/FavoriteOrganizeDlg.h
index 7c923b31af..baa24abbeb 100644
--- a/src/apps/mplayerc/FavoriteOrganizeDlg.h
+++ b/src/apps/mplayerc/FavoriteOrganizeDlg.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include "controls/DarkTabCtrl.h"
// CFavoriteOrganizeDlg dialog
@@ -42,7 +43,7 @@ class CFavoriteOrganizeDlg : public CResizableDialog
enum { IDD = IDD_FAVORGANIZE };
- CTabCtrl m_tab;
+ CDarkTabCtrl m_tab; // owner-drawn dark tab (falls back to native when the dark theme is off)
CListCtrl m_list;
protected:
diff --git a/src/apps/mplayerc/GoToDlg.cpp b/src/apps/mplayerc/GoToDlg.cpp
index 51b3544ffd..0735edabb0 100644
--- a/src/apps/mplayerc/GoToDlg.cpp
+++ b/src/apps/mplayerc/GoToDlg.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "GoToDlg.h"
+#include "controls/DarkTheme.h"
// CGoToDlg dialog
@@ -101,6 +102,8 @@ BOOL CGoToDlg::OnInitDialog()
}
}
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return FALSE;
}
diff --git a/src/apps/mplayerc/HistoryDlg.cpp b/src/apps/mplayerc/HistoryDlg.cpp
index 86d0e1cc6e..275f5607fe 100644
--- a/src/apps/mplayerc/HistoryDlg.cpp
+++ b/src/apps/mplayerc/HistoryDlg.cpp
@@ -23,6 +23,7 @@
#include "ItemPropertiesDlg.h"
#include "HistoryDlg.h"
#include "DSUtil/std_helper.h"
+#include "controls/DarkTheme.h"
// CHistoryDlg dialog
@@ -287,6 +288,8 @@ BOOL CHistoryDlg::OnInitDialog()
SHAutoComplete(m_FilterEdit, SHACF_AUTOAPPEND_FORCE_OFF | SHACF_AUTOSUGGEST_FORCE_OFF);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/ItemPropertiesDlg.cpp b/src/apps/mplayerc/ItemPropertiesDlg.cpp
index 6e48686c46..26d3139e31 100644
--- a/src/apps/mplayerc/ItemPropertiesDlg.cpp
+++ b/src/apps/mplayerc/ItemPropertiesDlg.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include
#include "ItemPropertiesDlg.h"
+#include "controls/DarkTheme.h"
// CItemPropertiesDlg
@@ -69,6 +70,8 @@ BOOL CItemPropertiesDlg::OnInitDialog()
SetMinTrackSize(r.Size());
SetMaxTrackSize({ 1000, r.Height() });
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/MainFrm.cpp b/src/apps/mplayerc/MainFrm.cpp
index 1199060bb7..3d6e4ee369 100644
--- a/src/apps/mplayerc/MainFrm.cpp
+++ b/src/apps/mplayerc/MainFrm.cpp
@@ -71,6 +71,8 @@
#include
#include "filters/ffmpeg_link_fix.h"
#include "ComPropertySheet.h"
+#include "controls/DarkTheme.h"
+#include "controls/DarkTabCtrl.h"
#include
#include
@@ -817,9 +819,17 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
// Hide all dockable bars by default
for (const auto& pDockingBar : m_dockingbars) {
+ // Draw the bar frame / gripper / floating-frame border dark, like the playlist bar. Without
+ // this the Shader Editor, Capture, Navigation and Subresync bars render their sizing frame
+ // with light system colours (a bright "white frame", docked or floating).
+ pDockingBar->m_bUseDarkTheme = s.bUseDarkTheme;
pDockingBar->ShowWindow(SW_HIDE);
}
+ // Dark-theme every standard message box shown on the UI thread (Reset/Export settings, error
+ // prompts, ...) without wrapping each call site. Follows the theme toggle; no-op when it's off.
+ DarkTheme::InstallMessageBoxHook();
+
m_fileDropTarget.Register(this);
GetDesktopWindow()->GetWindowRect(&m_rcDesktop);
@@ -9079,7 +9089,7 @@ void CMainFrame::OnMenuSubtitlesStyle()
CString caption = ResStr(IDS_SUBTITLES_STYLES);
caption.Replace(L"&", nullptr);
- CPropertySheet dlg(caption, GetModalParent());
+ CDarkPropertySheet dlg(caption, GetModalParent());
for (size_t i = 0; i < pages.size(); i++) {
dlg.AddPage(pages[i].get());
}
diff --git a/src/apps/mplayerc/MediaTypesDlg.cpp b/src/apps/mplayerc/MediaTypesDlg.cpp
index 5dd7719416..f702128df6 100644
--- a/src/apps/mplayerc/MediaTypesDlg.cpp
+++ b/src/apps/mplayerc/MediaTypesDlg.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "MediaTypesDlg.h"
+#include "controls/DarkTheme.h"
#include
@@ -77,6 +78,8 @@ BOOL CMediaTypesDlg::OnInitDialog()
SetMinTrackSize(CSize(300, 200));
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/OpenDlg.cpp b/src/apps/mplayerc/OpenDlg.cpp
index f6e1058cdf..c29754029e 100644
--- a/src/apps/mplayerc/OpenDlg.cpp
+++ b/src/apps/mplayerc/OpenDlg.cpp
@@ -25,6 +25,7 @@
#include "OpenDlg.h"
#include "FileDialogs.h"
#include "MainFrm.h"
+#include "controls/DarkTheme.h"
//
// COpenDlg dialog
@@ -142,6 +143,8 @@ BOOL COpenDlg::OnInitDialog()
pStat->SetIcon(m_hIcon);
}
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/PPageAccelTbl.cpp b/src/apps/mplayerc/PPageAccelTbl.cpp
index 0e64c34828..833b4748b8 100644
--- a/src/apps/mplayerc/PPageAccelTbl.cpp
+++ b/src/apps/mplayerc/PPageAccelTbl.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "PPageAccelTbl.h"
+#include "controls/DarkTheme.h"
#include "Misc.h"
//#define MASK_NUMBER 0xFFF
@@ -842,11 +843,20 @@ void CPPageAccelTbl::OnCustomdrawList( NMHDR* pNMHDR, LRESULT* pResult )
auto itemData = (ITEMDATA*)m_list.GetItemData(pLVCD->nmcd.dwItemSpec);
auto dup = itemData->flag;
- if (pLVCD->iSubItem == COL_CMD && dup
+ const bool isDup = (pLVCD->iSubItem == COL_CMD && dup
|| pLVCD->iSubItem == COL_KEY && (dup & DUP_KEY)
|| pLVCD->iSubItem == COL_APPCMD && (dup & DUP_APPCMD)
- || pLVCD->iSubItem == COL_RMCMD && (dup & DUP_RMCMD)) {
+ || pLVCD->iSubItem == COL_RMCMD && (dup & DUP_RMCMD));
+
+ if (isDup) {
pLVCD->clrTextBk = RGB(255, 130, 120);
+ if (DarkTheme::IsActive()) {
+ pLVCD->clrText = RGB(0, 0, 0); // keep the text readable on the highlight
+ }
+ }
+ else if (DarkTheme::IsActive()) {
+ pLVCD->clrTextBk = DarkTheme::FaceColor();
+ pLVCD->clrText = DarkTheme::TextColor();
}
else {
pLVCD->clrTextBk = GetSysColor(COLOR_WINDOW);
diff --git a/src/apps/mplayerc/PPageBase.cpp b/src/apps/mplayerc/PPageBase.cpp
index 8388c1a4e9..4edc14f472 100644
--- a/src/apps/mplayerc/PPageBase.cpp
+++ b/src/apps/mplayerc/PPageBase.cpp
@@ -22,6 +22,7 @@
#include "stdafx.h"
#include "MainFrm.h"
#include "PPageBase.h"
+#include "controls/DarkTheme.h"
// CPPageBase dialog
@@ -79,6 +80,9 @@ BOOL CPPageBase::PreTranslateMessage(MSG* pMsg)
BEGIN_MESSAGE_MAP(CPPageBase, CCmdUIPropertyPage)
ON_WM_DESTROY()
+ ON_WM_CTLCOLOR()
+ ON_WM_ERASEBKGND()
+ ON_WM_DRAWITEM()
END_MESSAGE_MAP()
// CPPageBase message handlers
@@ -87,7 +91,93 @@ BOOL CPPageBase::OnSetActive()
{
AfxGetAppSettings().nLastUsedPage = (UINT)(ULONG_PTR)m_pPSP->pszTemplate;
- return __super::OnSetActive();
+ BOOL bRet = __super::OnSetActive();
+
+ // Apply the dark visual style once, on first activation. Re-theming on every activation
+ // (SetWindowTheme per control + repaint) made switching pages sluggish; the controls stay
+ // themed for the page's lifetime, so once is enough.
+ if (!m_bDarkThemeApplied) {
+ DarkTheme::ApplyThemeToChildren(GetSafeHwnd());
+ m_bDarkThemeApplied = true;
+ }
+ // Keep group boxes behind the controls they frame so their dark fill never paints over them.
+ // Cheap (no SetWindowTheme), runs every activation so it self-heals after a runtime toggle.
+ DarkTheme::FixGroupBoxes(GetSafeHwnd());
+
+ return bRet;
+}
+
+HBRUSH CPPageBase::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = __super::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ if (HBRUSH hbrDark = DarkTheme::OnCtlColor(pDC, nCtlColor)) {
+ return hbrDark;
+ }
+
+ return hbr;
+}
+
+BOOL CPPageBase::OnEraseBkgnd(CDC* pDC)
+{
+ if (DarkTheme::IsActive()) {
+ CRect rc;
+ GetClientRect(rc);
+ pDC->FillSolidRect(rc, DarkTheme::FaceColor());
+ // Localized resources use SS_ETCHED* dividers, which paint a light 3D line dark mode never darkens.
+ // They are hidden while theming, so draw them here, on top of the background we just filled.
+ DarkTheme::DrawHiddenSeparators(GetSafeHwnd(), pDC);
+ return TRUE;
+ }
+
+ return __super::OnEraseBkgnd(pDC);
+}
+
+// The horizontal separator lines in the option pages are owner-drawn statics
+// (SS_OWNERDRAW) so Windows does not paint its light 3D etched line. We draw them
+// here: a flat line in the shared border colour when the dark theme is active, or
+// the classic etched edge otherwise.
+void CPPageBase::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
+{
+ if (lpDrawItemStruct && lpDrawItemStruct->CtlType == ODT_STATIC) {
+ CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
+ CRect rc(lpDrawItemStruct->rcItem);
+ if (DarkTheme::IsActive()) {
+ pDC->FillSolidRect(rc, DarkTheme::FaceColor());
+ pDC->FillSolidRect(rc.left, rc.top + rc.Height() / 2, rc.Width(), 1, DarkTheme::CtrlBorderColor());
+ } else {
+ pDC->FillSolidRect(rc, GetSysColor(COLOR_3DFACE));
+ ::DrawEdge(lpDrawItemStruct->hDC, &rc, EDGE_ETCHED, BF_TOP);
+ }
+ return;
+ }
+
+ __super::OnDrawItem(nIDCtl, lpDrawItemStruct);
+}
+
+BOOL CPPageBase::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
+{
+ // For NM_CUSTOMDRAW, let any page-specific handler run first (e.g. colour-picker
+ // buttons that paint themselves with the selected colour, or list controls). Only
+ // when the page does not handle it do we apply the generic dark theming for the
+ // controls native dark mode leaves light: trackbar (slider) channels, checkbox/
+ // radio-button captions and push buttons. (Group boxes are handled by subclassing,
+ // since they emit no NM_CUSTOMDRAW.)
+ NMHDR* pNMHDR = reinterpret_cast(lParam);
+ if (pNMHDR && pNMHDR->code == NM_CUSTOMDRAW) {
+ if (__super::OnNotify(wParam, lParam, pResult)) {
+ return TRUE;
+ }
+ if (DarkTheme::TrackbarCustomDraw(pNMHDR, pResult)) {
+ return TRUE;
+ }
+ if (DarkTheme::ButtonCustomDraw(pNMHDR, pResult)) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ return __super::OnNotify(wParam, lParam, pResult);
}
void CPPageBase::OnDestroy()
diff --git a/src/apps/mplayerc/PPageBase.h b/src/apps/mplayerc/PPageBase.h
index 6cea299149..7f142b46f9 100644
--- a/src/apps/mplayerc/PPageBase.h
+++ b/src/apps/mplayerc/PPageBase.h
@@ -34,6 +34,9 @@ class CPPageBase : public CCmdUIPropertyPage
CToolTipCtrl m_wndToolTip;
void CreateToolTip();
+ // dark theme: apply the dark visual style to child controls once, on first activation
+ bool m_bDarkThemeApplied = false;
+
public:
CPPageBase(UINT nIDTemplate, UINT nIDCaption = 0);
virtual ~CPPageBase();
@@ -43,6 +46,7 @@ class CPPageBase : public CCmdUIPropertyPage
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnSetActive();
virtual BOOL OnApply();
+ virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
int ScaleY(int y);
@@ -50,4 +54,7 @@ class CPPageBase : public CCmdUIPropertyPage
public:
afx_msg void OnDestroy();
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+ afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+ afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
};
diff --git a/src/apps/mplayerc/PPageExternalFilters.h b/src/apps/mplayerc/PPageExternalFilters.h
index a5da197fcf..947f8477bd 100644
--- a/src/apps/mplayerc/PPageExternalFilters.h
+++ b/src/apps/mplayerc/PPageExternalFilters.h
@@ -23,6 +23,7 @@
#include "PPageBase.h"
#include "controls/FloatEdit.h"
+#include "controls/DarkCheckListBox.h"
bool IsSupportedExternalVideoRenderer(CLSID clsid);
@@ -46,7 +47,7 @@ class CPPageExternalFilters : public CPPageBase
enum { IDD = IDD_PPAGEEXTERNALFILTERS };
- CCheckListBox m_filters;
+ CDarkCheckListBox m_filters;
int m_iLoadType = FilterOverride::PREFERRED;
CHexEdit m_dwMerit;
CTreeCtrl m_tree;
diff --git a/src/apps/mplayerc/PPageFileInfoClip.cpp b/src/apps/mplayerc/PPageFileInfoClip.cpp
index 054caf217f..19cf6faaac 100644
--- a/src/apps/mplayerc/PPageFileInfoClip.cpp
+++ b/src/apps/mplayerc/PPageFileInfoClip.cpp
@@ -22,6 +22,7 @@
#include "stdafx.h"
#include "MainFrm.h"
#include "PPageFileInfoClip.h"
+#include "controls/DarkTheme.h"
#include
@@ -232,6 +233,8 @@ BOOL CPPageFileInfoClip::OnInitDialog()
UpdateData(FALSE);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/PPageFileInfoDetails.cpp b/src/apps/mplayerc/PPageFileInfoDetails.cpp
index 4b71282c3e..819d01571d 100644
--- a/src/apps/mplayerc/PPageFileInfoDetails.cpp
+++ b/src/apps/mplayerc/PPageFileInfoDetails.cpp
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "PPageFileInfoDetails.h"
+#include "controls/DarkTheme.h"
#include
#include
#include
@@ -229,6 +230,8 @@ BOOL CPPageFileInfoDetails::OnInitDialog()
m_encoding.SetWindowTextW(m_encodingText);
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/PPageFileInfoSheet.cpp b/src/apps/mplayerc/PPageFileInfoSheet.cpp
index 204f154b2c..6bd1c01529 100644
--- a/src/apps/mplayerc/PPageFileInfoSheet.cpp
+++ b/src/apps/mplayerc/PPageFileInfoSheet.cpp
@@ -24,6 +24,11 @@
#include "PPageFileInfoSheet.h"
#include "PPageFileMediaInfo.h"
#include "FileDialogs.h"
+#include "controls/DarkTheme.h"
+
+// windowsx.h defines a function-like SubclassWindow(hwnd, lpfn) macro that collides with
+// CWnd::SubclassWindow(HWND); undef it so the MFC method call below parses correctly.
+#undef SubclassWindow
IMPLEMENT_DYNAMIC(CMPCPropertySheet, CPropertySheet)
CMPCPropertySheet::CMPCPropertySheet(LPCWSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
@@ -120,6 +125,16 @@ BOOL CPPageFileInfoSheet::OnInitDialog()
ModifyStyle(0, WS_MAXIMIZEBOX);
+ // Dark-theme the property-sheet frame + the two dynamically-created MediaInfo buttons, and attach
+ // the owner-drawn dark tab (the stock tab keeps a light body/frame; it falls back to native when
+ // the theme is off). Each page dark-themes itself in its own OnInitDialog.
+ if (DarkTheme::IsActive()) {
+ if (CTabCtrl* pTab = GetTabControl()) {
+ m_dark_tab.SubclassWindow(pTab->GetSafeHwnd());
+ }
+ }
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
for (int i = 0; i < GetPageCount(); i++) {
DWORD nID = GetResourceId(i);
if (nID == s.nLastFileInfoPage) {
diff --git a/src/apps/mplayerc/PPageFileInfoSheet.h b/src/apps/mplayerc/PPageFileInfoSheet.h
index b587419663..75e0b5b389 100644
--- a/src/apps/mplayerc/PPageFileInfoSheet.h
+++ b/src/apps/mplayerc/PPageFileInfoSheet.h
@@ -26,6 +26,7 @@
#include "PPageFileInfoDetails.h"
#include "PPageFileInfoRes.h"
#include "PPageFileMediaInfo.h"
+#include "controls/DarkTabCtrl.h"
class CMainFrame;
@@ -75,6 +76,8 @@ class CPPageFileInfoSheet : public CMPCPropertySheet, public CDPI
CButton m_Button_MI_SaveAs;
CButton m_Button_MI_Clipboard;
+ CDarkTabCtrl m_dark_tab; // owner-drawn dark tab attached in OnInitDialog when the dark theme is on
+
BOOL m_bNeedInit = TRUE;
CRect m_rCrt;
CRect m_rWnd;
diff --git a/src/apps/mplayerc/PPageFileMediaInfo.cpp b/src/apps/mplayerc/PPageFileMediaInfo.cpp
index b9fd91ecc2..9c02ff0b43 100644
--- a/src/apps/mplayerc/PPageFileMediaInfo.cpp
+++ b/src/apps/mplayerc/PPageFileMediaInfo.cpp
@@ -22,6 +22,7 @@
#include "MainFrm.h"
#include "PPageFileMediaInfo.h"
#include "DSUtil/FileHandle.h"
+#include "controls/DarkTheme.h"
static MediaInfoLib::String mi_get_lang_file()
{
@@ -117,6 +118,8 @@ BOOL CPPageFileMediaInfo::OnInitDialog()
}
OnComboFileChange();
+ DarkTheme::ThemeDialog(GetSafeHwnd());
+
return TRUE;
}
diff --git a/src/apps/mplayerc/PPageFormats.cpp b/src/apps/mplayerc/PPageFormats.cpp
index fb64da19f1..762f9711b8 100644
--- a/src/apps/mplayerc/PPageFormats.cpp
+++ b/src/apps/mplayerc/PPageFormats.cpp
@@ -27,11 +27,165 @@
#include "PPageFormats.h"
#include "SvgHelper.h"
#include "WindowsUserChoice.h"
+#include "controls/DarkTheme.h"
+#include
+#include // BP_CHECKBOX / CBS_*
+#pragma comment(lib, "uxtheme.lib")
static constexpr auto previousRegistration = L"PreviousRegistration";
static constexpr auto registeredAppName = L"MPC-BE";
static constexpr auto registeredKey = L"Software\\Clients\\Media\\MPC-BE\\Capabilities";
+// Renders the native themed dark checkbox — the same celeste-accent glyph the rest of
+// the dark Options dialog uses (Internal Filters, Fullscreen) — into a 3-state image
+// list: index 0 = unchecked, 1 = checked, 2 = indeterminate.
+// The glyph is drawn directly over the opaque dark row background (exactly like the
+// Fullscreen page draws it with DrawThemeBackground). Going through buffered paint /
+// premultiplied alpha instead darkened the celeste and left the unchecked box white.
+// Because the app runs in force-dark mode, OpenThemeData(..., "BUTTON") resolves to the
+// dark (celeste) checkbox.
+static bool MakeThemedCheckImageList(CImageList& il, int h, HWND hRef, bool bDark)
+{
+ if (bDark) {
+ DarkTheme::AllowDarkModeForApp(); // force-dark app mode
+ DarkTheme::ApplyThemeToControl(hRef); // allow dark mode on the list *now* — this runs at
+ // OnInitDialog time, before the page's OnSetActive
+ // themes it, so OpenThemeData(hRef, "BUTTON") resolves
+ // to the dark checkbox (celeste check), not the light one.
+ }
+ // In light mode the control keeps its default theme, so OpenThemeData resolves to the native
+ // light checkbox glyph — the same one the other checklists show when the dark theme is off.
+
+ HTHEME hTheme = ::OpenThemeData(hRef, L"BUTTON");
+ if (!hTheme) {
+ return false;
+ }
+
+ il.DeleteImageList();
+ if (!il.Create(h, h, ILC_COLOR32, 3, 0)) {
+ ::CloseThemeData(hTheme);
+ return false;
+ }
+
+ // Buffered painting into a 32-bit top-down DIB keeps the glyph's per-pixel alpha, so
+ // the rounded corners stay transparent (a plain opaque draw left 4 white corner dots).
+ ::BufferedPaintInit();
+
+ const int states[3] = { CBS_UNCHECKEDNORMAL, CBS_CHECKEDNORMAL, CBS_MIXEDNORMAL };
+ HDC hdcScreen = ::GetDC(nullptr);
+ HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
+ bool ok = true;
+
+ for (int i = 0; i < 3 && ok; ++i) {
+ BITMAPINFO bmi = {};
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = h;
+ bmi.bmiHeader.biHeight = -h; // top-down
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ void* pBits = nullptr;
+ HBITMAP hDib = ::CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
+ if (!hDib) {
+ ok = false;
+ break;
+ }
+ HBITMAP hOldBmp = (HBITMAP)::SelectObject(hdcMem, hDib);
+
+ RECT rcFull = { 0, 0, h, h };
+ BP_PAINTPARAMS pp = { sizeof(pp) };
+ pp.dwFlags = BPPF_ERASE;
+ HDC hdcPaint = nullptr;
+ HPAINTBUFFER hbp = ::BeginBufferedPaint(hdcMem, &rcFull, BPBF_TOPDOWNDIB, &pp, &hdcPaint);
+ if (hbp) {
+ SIZE gsz = { h, h };
+ ::GetThemePartSize(hTheme, hdcPaint, BP_CHECKBOX, states[i], nullptr, TS_DRAW, &gsz);
+ // Clamp to the cell so the glyph is never clipped (its natural size can be a
+ // couple of pixels larger than the cell, which cut off part of the checkbox).
+ int sz = std::min(std::min((int)gsz.cx, (int)gsz.cy), h);
+ if (sz < 1) { sz = h; }
+ RECT rg;
+ rg.left = (h - sz) / 2;
+ rg.top = (h - sz) / 2;
+ rg.right = rg.left + sz;
+ rg.bottom = rg.top + sz;
+ ::DrawThemeBackground(hTheme, hdcPaint, BP_CHECKBOX, states[i], &rg, nullptr);
+ ::EndBufferedPaint(hbp, TRUE); // copy the premultiplied bits into hDib
+ } else {
+ ok = false;
+ }
+
+ ::SelectObject(hdcMem, hOldBmp);
+ if (ok) {
+ il.Add(CBitmap::FromHandle(hDib), (CBitmap*)nullptr); // 32bpp: keeps per-pixel alpha
+ }
+ ::DeleteObject(hDib);
+ }
+
+ ::DeleteDC(hdcMem);
+ ::ReleaseDC(nullptr, hdcScreen);
+ ::BufferedPaintUnInit();
+ ::CloseThemeData(hTheme);
+
+ if (ok && il.GetImageCount() == 3) {
+ return true;
+ }
+ il.DeleteImageList();
+ return false;
+}
+
+// Fallback (used only if the themed glyph above is unavailable): a hand-drawn dark box
+// with a celeste checkmark, matching the palette of the rest of the dark dialog.
+static void MakeDarkCheckImageList(CImageList& il, int h, HWND hRef)
+{
+ if (MakeThemedCheckImageList(il, h, hRef, true)) {
+ return;
+ }
+
+ const COLORREF mask = RGB(255, 0, 255); // transparent key colour
+ const COLORREF back = DarkTheme::CtrlBackColor();
+ const COLORREF border = DarkTheme::CtrlBorderColor();
+ const COLORREF mark = RGB(76, 194, 255); // celeste, Win11 dark-mode accent glyph
+
+ CClientDC screen(nullptr);
+ CDC dc;
+ dc.CreateCompatibleDC(&screen);
+ CBitmap bmp;
+ bmp.CreateCompatibleBitmap(&screen, h * 3, h);
+ CBitmap* pOld = dc.SelectObject(&bmp);
+
+ dc.FillSolidRect(0, 0, h * 3, h, mask);
+
+ for (int i = 0; i < 3; ++i) {
+ CRect box(i * h, 0, i * h + h, h);
+ box.DeflateRect(1, 1);
+ dc.FillSolidRect(box, back);
+ dc.Draw3dRect(box, border, border);
+
+ if (i == 1) {
+ // checkmark
+ CPen pen(PS_SOLID, 2, mark);
+ CPen* pp = dc.SelectObject(&pen);
+ const int l = box.left, t = box.top, w = box.Width(), hh = box.Height();
+ dc.MoveTo(l + w * 3 / 10, t + hh * 5 / 10);
+ dc.LineTo(l + w * 45 / 100, t + hh * 7 / 10);
+ dc.LineTo(l + w * 75 / 100, t + hh * 28 / 100);
+ dc.SelectObject(pp);
+ } else if (i == 2) {
+ // partial: a small filled square
+ CRect inner = box;
+ inner.DeflateRect(box.Width() / 4, box.Height() / 4);
+ dc.FillSolidRect(inner, mark);
+ }
+ }
+
+ dc.SelectObject(pOld);
+ il.DeleteImageList();
+ il.Create(h, h, ILC_COLOR24 | ILC_MASK, 3, 0);
+ il.Add(&bmp, mask);
+}
+
// CPPageFormats dialog
CComPtr CPPageFormats::m_pAAR;
@@ -78,22 +232,16 @@ void CPPageFormats::DoDataExchange(CDataExchange* pDX)
int CPPageFormats::GetChecked(int iItem)
{
- LVITEM lvi;
- lvi.iItem = iItem;
- lvi.iSubItem = 0;
- lvi.mask = LVIF_IMAGE;
- m_list.GetItem(&lvi);
- return(lvi.iImage);
+ // The check state lives in the item's state-image index (1-based). Using the state image
+ // instead of the small icon keeps the checkbox out of the row's selection highlight, like a
+ // native list-view checkbox (the small icon shares the item cell and gets the highlight).
+ const UINT st = m_list.GetItemState(iItem, LVIS_STATEIMAGEMASK);
+ return (int)(st >> 12) - 1; // 0 = none, 1 = all, 2 = partial
}
void CPPageFormats::SetChecked(int iItem, int iChecked)
{
- LVITEM lvi;
- lvi.iItem = iItem;
- lvi.iSubItem = 0;
- lvi.mask = LVIF_IMAGE;
- lvi.iImage = iChecked;
- m_list.SetItem(&lvi);
+ m_list.SetItemState(iItem, INDEXTOSTATEIMAGEMASK(iChecked + 1), LVIS_STATEIMAGEMASK);
}
CString CPPageFormats::GetEnqueueCommand()
@@ -665,6 +813,56 @@ END_MESSAGE_MAP()
// CPPageFormats message handlers
+void CPPageFormats::BuildCheckImageList()
+{
+ int chkH = 0;
+ if (CDPI* pDpi = dynamic_cast(AfxGetMainWnd())) {
+ chkH = pDpi->ScaleY(12);
+ } else {
+ // this panel can be created without the main window.
+ CDPI dpi;
+ chkH = dpi.ScaleY(12);
+ }
+
+ m_onoff.DeleteImageList(); // rebuildable: re-run when the dark theme is toggled at runtime
+
+ if (DarkTheme::IsActive()) {
+ MakeDarkCheckImageList(m_onoff, chkH, m_list.GetSafeHwnd());
+ } else if (!MakeThemedCheckImageList(m_onoff, chkH, m_list.GetSafeHwnd(), false)) {
+ // Native (themed) light checkboxes matching the other checklists; fall back to upstream's
+ // SVG glyphs only if the visual style is unavailable.
+ CSvgImage svgImage;
+ if (svgImage.Load(IDF_SVG_ONOFF)) {
+ int w = 0;
+ int h = chkH;
+ if (HBITMAP hBitmap = svgImage.Rasterize(w, h)) {
+ if (w == h * 3) {
+ m_onoff.Create(h, h, ILC_COLOR32 | ILC_MASK, 3, 0);
+ ImageList_Add(m_onoff.GetSafeHandle(), hBitmap, nullptr);
+ }
+ DeleteObject(hBitmap);
+ }
+ }
+ }
+
+ m_list.SetImageList(&m_onoff, LVSIL_STATE);
+ m_onoffDark = DarkTheme::IsActive();
+}
+
+BOOL CPPageFormats::OnSetActive()
+{
+ const BOOL bRet = __super::OnSetActive();
+
+ // The checkbox glyphs are baked into an image list at init time; if the dark theme was
+ // toggled since then, rebuild them so unchecked boxes don't stay dark on a light dialog.
+ if (m_onoffDark != DarkTheme::IsActive()) {
+ BuildCheckImageList();
+ m_list.Invalidate();
+ }
+
+ return bRet;
+}
+
BOOL CPPageFormats::OnInitDialog()
{
__super::OnInitDialog();
@@ -677,27 +875,7 @@ BOOL CPPageFormats::OnInitDialog()
m_list.InsertColumn(COL_CATEGORY, L"Category", LVCFMT_LEFT);
- CSvgImage svgImage;
- if (svgImage.Load(IDF_SVG_ONOFF)) {
- int w = 0;
- int h = 0;
- if (CDPI* pDpi = dynamic_cast(AfxGetMainWnd())) {
- h = pDpi->ScaleY(12);
- } else {
- // this panel can be created without the main window.
- CDPI dpi;
- h = dpi.ScaleY(12);
- }
- if (HBITMAP hBitmap = svgImage.Rasterize(w, h)) {
- if (w == h * 3) {
- m_onoff.Create(h, h, ILC_COLOR32 | ILC_MASK, 3, 0);
- ImageList_Add(m_onoff.GetSafeHandle(), hBitmap, nullptr);
- }
- DeleteObject(hBitmap);
- }
- }
-
- m_list.SetImageList(&m_onoff, LVSIL_SMALL);
+ BuildCheckImageList();
CMediaFormats& mf = AfxGetAppSettings().m_Formats;
mf.UpdateData(false);
@@ -713,6 +891,15 @@ BOOL CPPageFormats::OnInitDialog()
}
m_list.SetColumnWidth(COL_CATEGORY, LVSCW_AUTOSIZE);
+ {
+ // Stretch the single column to at least the list width so the content (and the
+ // full-row selection highlight) fills the control instead of stopping mid-way.
+ CRect rcList;
+ m_list.GetClientRect(rcList);
+ if (m_list.GetColumnWidth(COL_CATEGORY) < rcList.Width()) {
+ m_list.SetColumnWidth(COL_CATEGORY, rcList.Width());
+ }
+ }
m_list.SetSelectionMark(0);
m_list.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
@@ -752,6 +939,7 @@ BOOL CPPageFormats::OnInitDialog()
GetDlgItem(IDC_BUTTON5)->ShowWindow(SW_SHOW);
GetDlgItem(IDC_BUTTON5)->SendMessageW(BCM_SETSHIELD, 0, 1);
+ DarkTheme::MarkUacShield(GetDlgItem(IDC_BUTTON5)->GetSafeHwnd());
m_bInsufficientPrivileges = true;
} else {
@@ -1067,9 +1255,10 @@ void CPPageFormats::OnNMClickList1(NMHDR* pNMHDR, LRESULT* pResult)
LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)pNMHDR;
if (lpnmlv->iItem >= 0 && lpnmlv->iSubItem == COL_CATEGORY) {
- CRect r;
- m_list.GetItemRect(lpnmlv->iItem, r, LVIR_ICON);
- if (r.PtInRect(lpnmlv->ptAction)) {
+ LVHITTESTINFO hti = {};
+ hti.pt = lpnmlv->ptAction;
+ m_list.HitTest(&hti);
+ if (hti.flags & LVHT_ONITEMSTATEICON) {
if (m_bInsufficientPrivileges) {
MessageBoxW(ResStr (IDS_CANNOT_CHANGE_FORMAT));
} else {
diff --git a/src/apps/mplayerc/PPageFormats.h b/src/apps/mplayerc/PPageFormats.h
index 90a908db43..8050261f63 100644
--- a/src/apps/mplayerc/PPageFormats.h
+++ b/src/apps/mplayerc/PPageFormats.h
@@ -38,9 +38,12 @@ class CPPageFormats : public CPPageBase
private:
CImageList m_onoff;
+ bool m_onoffDark = false; // the dark-theme state the m_onoff checkbox glyphs were built for
bool m_bInsufficientPrivileges;
bool m_bFileExtChanged;
+ void BuildCheckImageList(); // (re)builds the checkbox glyph image list for the current theme
+
int GetChecked(int iItem);
void SetChecked(int iItem, int fChecked);
@@ -96,6 +99,7 @@ class CPPageFormats : public CPPageBase
protected:
virtual void DoDataExchange(CDataExchange* pDX);
virtual BOOL OnInitDialog();
+ virtual BOOL OnSetActive();
virtual BOOL OnApply();
DECLARE_MESSAGE_MAP()
diff --git a/src/apps/mplayerc/PPageFullscreen.cpp b/src/apps/mplayerc/PPageFullscreen.cpp
index 060231a3f3..673c94b580 100644
--- a/src/apps/mplayerc/PPageFullscreen.cpp
+++ b/src/apps/mplayerc/PPageFullscreen.cpp
@@ -22,10 +22,14 @@
#include "stdafx.h"
#include "MainFrm.h"
#include "PPageFullscreen.h"
+#include "controls/DarkTheme.h"
#include "DSUtil/SysVersion.h"
#include "DSUtil/std_helper.h"
#include "MultiMonitor.h"
#include