vcl/Library_vclplug_win.mk | 1 vcl/inc/win/salframe.h | 2 vcl/inc/win/salinst.h | 1 vcl/win/app/salinst.cxx | 49 +++++++++++++++ vcl/win/gdi/salnativewidgets-luna.cxx | 61 +++++++++++++++++++ vcl/win/window/salframe.cxx | 107 ++++++++++++++++++++++++++++------ 6 files changed, 204 insertions(+), 17 deletions(-)
New commits: commit a3f400886768bf95fbd8e6b236e11d7aac393b96 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Sat Mar 12 20:38:21 2022 +0000 Commit: Caolán McNamara <caol...@redhat.com> CommitDate: Wed Mar 16 22:16:01 2022 +0100 Related: tdf#118320 enable some windows dark theme support If experimental mode is enabled and only for windows >= 10.0.18362 Toggling from dark to light mode is detected, and getting some suitable colors for dark mode works. The titlebar will toggle into dark/light mode successfully. DrawThemeBackground does something sensible for buttons and scrollbar at least, comboboxes can be forced to work. SpinButtons are less successful. Menubar and toolbar just bodged to draw a solid backcolor. Notebooks/TabControls don't respect the theme. Its possible it makes more sense to not try and use the windows control drawings apis and just set some dark colors and draw with the old built-in vcl widget rendering or alternative. Change-Id: Ic7927fb9af7ec94444d9de6e0204560e2789be46 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131453 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caol...@redhat.com> diff --git a/vcl/Library_vclplug_win.mk b/vcl/Library_vclplug_win.mk index 65326727033b..2627914cc15c 100644 --- a/vcl/Library_vclplug_win.mk +++ b/vcl/Library_vclplug_win.mk @@ -118,6 +118,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_win,\ $(eval $(call gb_Library_use_system_win32_libs,vclplug_win,\ advapi32 \ d2d1 \ + dwmapi \ dwrite \ gdi32 \ gdiplus \ diff --git a/vcl/inc/win/salframe.h b/vcl/inc/win/salframe.h index de0f3b37c1ff..0cce466b3173 100644 --- a/vcl/inc/win/salframe.h +++ b/vcl/inc/win/salframe.h @@ -142,6 +142,8 @@ public: void ImplSalGetWorkArea( HWND hWnd, RECT *pRect, const RECT *pParentRect ); +bool UseDarkMode(); + // get foreign key names namespace vcl_sal { OUString getKeysReplacementName( diff --git a/vcl/inc/win/salinst.h b/vcl/inc/win/salinst.h index c312b8b18c14..c62a68ae0455 100644 --- a/vcl/inc/win/salinst.h +++ b/vcl/inc/win/salinst.h @@ -84,6 +84,7 @@ public: SalFrame* ImplSalCreateFrame( WinSalInstance* pInst, HWND hWndParent, SalFrameStyleFlags nSalFrameStyle ); SalObject* ImplSalCreateObject( WinSalInstance* pInst, WinSalFrame* pParent ); HWND ImplSalReCreateHWND( HWND hWndParent, HWND oldhWnd, bool bAsChild ); +bool OSSupportsDarkMode(); #endif // INCLUDED_VCL_INC_WIN_SALINST_H diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx index e9d8e73f8a5e..a322046d66bf 100644 --- a/vcl/win/app/salinst.cxx +++ b/vcl/win/app/salinst.cxx @@ -318,6 +318,43 @@ SalData::~SalData() Gdiplus::GdiplusShutdown(gdiplusToken); } +bool OSSupportsDarkMode() +{ + bool bRet = false; + if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll")) + { + typedef LONG(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW); + if (auto RtlGetVersion + = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion"))) + { + RTL_OSVERSIONINFOW vi2{}; + vi2.dwOSVersionInfoSize = sizeof(vi2); + if (RtlGetVersion(&vi2) == 0) + { + if (vi2.dwMajorVersion > 10) + bRet = true; + else if (vi2.dwMajorVersion == 10) + { + if (vi2.dwMinorVersion > 0) + bRet = true; + else if (vi2.dwBuildNumber >= 18362) + bRet = true; + } + } + } + } + return bRet; +} + +enum PreferredAppMode +{ + Default, + AllowDark, + ForceDark, + ForceLight, + Max +}; + extern "C" { VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance() { @@ -331,6 +368,18 @@ VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance() pSalData->mnAppThreadId = GetCurrentThreadId(); + static bool bSetAllowDarkMode = OSSupportsDarkMode(); // too early to additionally check LibreOffice's config + if (bSetAllowDarkMode) + { + typedef PreferredAppMode(WINAPI* SetPreferredAppMode_t)(PreferredAppMode); + if (HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)) + { + if (auto SetPreferredAppMode = reinterpret_cast<SetPreferredAppMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(135)))) + SetPreferredAppMode(AllowDark); + FreeLibrary(hUxthemeLib); + } + } + // register frame class WNDCLASSEXW aWndClassEx; aWndClassEx.cbSize = sizeof( aWndClassEx ); diff --git a/vcl/win/gdi/salnativewidgets-luna.cxx b/vcl/win/gdi/salnativewidgets-luna.cxx index 8e754d129580..c4113b877f1f 100644 --- a/vcl/win/gdi/salnativewidgets-luna.cxx +++ b/vcl/win/gdi/salnativewidgets-luna.cxx @@ -36,6 +36,7 @@ #include <osl/diagnose.h> #include <osl/module.h> #include <o3tl/char16_t2wchar_t.hxx> +#include <officecfg/Office/Common.hxx> #include <vcl/svapp.hxx> #include <vcl/settings.hxx> @@ -395,6 +396,26 @@ static bool implDrawNativeMenuMark(HDC hDC, HTHEME hTheme, RECT rc, ControlPart return ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECK, iState, rc, aCaption); } +bool UseDarkMode() +{ + static bool bExperimental = officecfg::Office::Common::Misc::ExperimentalMode::get(); + if (!bExperimental) + return false; + + HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!hUxthemeLib) + return false; + + bool bRet = false; + typedef bool(WINAPI* ShouldAppsUseDarkMode_t)(); + if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132)))) + bRet = ShouldAppsUseDarkMode(); + + FreeLibrary(hUxthemeLib); + + return bRet; +} + static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc, ControlType nType, ControlPart nPart, @@ -819,6 +840,17 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc, rc.top = 0; // extend potential gradient to cover menu bar as well } + // menubar in main window gets drawn in white in "darkmode", so bodge this here + if (UseDarkMode()) + { + Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor()); + ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(), + aColor.GetGreen(), + aColor.GetBlue()))); + FillRect(hDC, &rc, hbrush.get()); + return true; + } + // make it more compatible with Aero if( ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames ) { @@ -839,6 +871,17 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc, const MenubarValue *pValue = static_cast<const MenubarValue*>(&aValue); rc.bottom += pValue->maTopDockingAreaHeight; // extend potential gradient to cover docking area as well + // menubar in main window gets drawn in white in "darkmode", so bodge this here + if (UseDarkMode()) + { + Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor()); + ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(), + aColor.GetGreen(), + aColor.GetBlue()))); + FillRect(hDC, &rc, hbrush.get()); + return true; + } + // make it more compatible with Aero if( ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames ) { @@ -1045,6 +1088,10 @@ bool WinSalGraphics::drawNativeControl( ControlType nType, return true; } + const bool bUseDarkMode = UseDarkMode(); + if (bUseDarkMode) + SetWindowTheme(mhWnd, L"Explorer", nullptr); + switch( nType ) { case ControlType::Pushbutton: @@ -1059,7 +1106,11 @@ bool WinSalGraphics::drawNativeControl( ControlType nType, if( nPart == ControlPart::Entire ) hTheme = getThemeHandle(mhWnd, L"Edit", mpImpl.get()); else if( nPart == ControlPart::ButtonDown ) + { + if (bUseDarkMode) + SetWindowTheme(mhWnd, L"CFD", nullptr); hTheme = getThemeHandle(mhWnd, L"Combobox", mpImpl.get()); + } break; case ControlType::Spinbox: if( nPart == ControlPart::Entire ) @@ -1078,7 +1129,11 @@ bool WinSalGraphics::drawNativeControl( ControlType nType, if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow ) hTheme = getThemeHandle(mhWnd, L"Listview", mpImpl.get()); else if( nPart == ControlPart::ButtonDown ) + { + if (bUseDarkMode) + SetWindowTheme(mhWnd, L"CFD", nullptr); hTheme = getThemeHandle(mhWnd, L"Combobox", mpImpl.get()); + } break; case ControlType::TabPane: case ControlType::TabBody: @@ -1129,7 +1184,11 @@ bool WinSalGraphics::drawNativeControl( ControlType nType, } if( !hTheme ) + { + if (bUseDarkMode) + SetWindowTheme(mhWnd, nullptr, nullptr); return false; + } RECT rc; rc.left = buttonRect.Left(); @@ -1167,6 +1226,8 @@ bool WinSalGraphics::drawNativeControl( ControlType nType, } } + if (bUseDarkMode) + SetWindowTheme(mhWnd, nullptr, nullptr); return bOk; } diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx index 95b9a36693ad..5965e9225058 100644 --- a/vcl/win/window/salframe.cxx +++ b/vcl/win/window/salframe.cxx @@ -94,10 +94,13 @@ # define WIN32_LEAN_AND_MEAN #endif #include <windows.h> +#include <dwmapi.h> #include <shobjidl.h> #include <propkey.h> #include <propvarutil.h> #include <shellapi.h> +#include <uxtheme.h> +#include <Vssym32.h> using namespace ::com::sun::star; using namespace ::com::sun::star::uno; @@ -277,6 +280,33 @@ void ImplSalGetWorkArea( HWND hWnd, RECT *pRect, const RECT *pParentRect ) } } +static void UpdateDarkMode(HWND hWnd) +{ + static bool bExperimental = officecfg::Office::Common::Misc::ExperimentalMode::get(); + if (!bExperimental) + return; + static bool bOSSupportsDarkMode = OSSupportsDarkMode(); + if (!bOSSupportsDarkMode) + return; + + HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!hUxthemeLib) + return; + + typedef void(WINAPI* AllowDarkModeForWindow_t)(HWND, BOOL); + if (auto AllowDarkModeForWindow = reinterpret_cast<AllowDarkModeForWindow_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(133)))) + AllowDarkModeForWindow(hWnd, TRUE); + + typedef bool(WINAPI* ShouldAppsUseDarkMode_t)(); + if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132)))) + { + BOOL bDarkMode = ShouldAppsUseDarkMode(); + DwmSetWindowAttribute(hWnd, 20, &bDarkMode, sizeof(bDarkMode)); + } + + FreeLibrary(hUxthemeLib); +} + SalFrame* ImplSalCreateFrame( WinSalInstance* pInst, HWND hWndParent, SalFrameStyleFlags nSalFrameStyle ) { @@ -2627,7 +2657,61 @@ void WinSalFrame::UpdateSettings( AllSettings& rSettings ) aStyleSettings.SetActiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVEBORDER ) ) ); aStyleSettings.SetDeactiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVEBORDER ) ) ); aStyleSettings.SetDeactiveColor( ImplWinColorToSal( GetSysColor( COLOR_GRADIENTINACTIVECAPTION ) ) ); - aStyleSettings.SetFaceColor( ImplWinColorToSal( GetSysColor( COLOR_3DFACE ) ) ); + + Color aControlTextColor; + Color aMenuBarTextColor; + Color aMenuBarRolloverTextColor; + + if (UseDarkMode()) + { + SetWindowTheme(mhWnd, L"Explorer", nullptr); + + HTHEME hTheme = OpenThemeData(nullptr, L"ItemsView"); + COLORREF color; + GetThemeColor(hTheme, 0, 0, TMT_FILLCOLOR, &color); + aStyleSettings.SetFaceColor( ImplWinColorToSal( color ) ); + aStyleSettings.SetWindowColor( ImplWinColorToSal( color ) ); + GetThemeColor(hTheme, 0, 0, TMT_TEXTCOLOR, &color); + aStyleSettings.SetWindowTextColor( ImplWinColorToSal( color ) ); + CloseThemeData(hTheme); + + hTheme = OpenThemeData(mhWnd, L"Button"); + GetThemeColor(hTheme, BP_PUSHBUTTON, MBI_NORMAL, TMT_TEXTCOLOR, &color); + aControlTextColor = ImplWinColorToSal(color); + GetThemeColor(hTheme, BP_CHECKBOX, MBI_NORMAL, TMT_TEXTCOLOR, &color); + aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( color ) ); + CloseThemeData(hTheme); + + SetWindowTheme(mhWnd, nullptr, nullptr); + + hTheme = OpenThemeData(mhWnd, L"Menu"); + GetThemeColor(hTheme, MENU_POPUPITEM, MBI_NORMAL, TMT_TEXTCOLOR, &color); + aStyleSettings.SetMenuTextColor( ImplWinColorToSal( color ) ); + aMenuBarTextColor = ImplWinColorToSal( color ); + aMenuBarRolloverTextColor = ImplWinColorToSal( color ); + CloseThemeData(hTheme); + } + else + { + aStyleSettings.SetFaceColor( ImplWinColorToSal( GetSysColor( COLOR_3DFACE ) ) ); + aStyleSettings.SetWindowColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOW ) ) ); + aStyleSettings.SetWindowTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); + aControlTextColor = ImplWinColorToSal(GetSysColor(COLOR_BTNTEXT)); + aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); + aStyleSettings.SetMenuTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) ); + aMenuBarTextColor = ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ); + aMenuBarRolloverTextColor = ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHTTEXT ) ); + } + + if ( std::optional<Color> aColor = aStyleSettings.GetPersonaMenuBarTextColor() ) + { + aMenuBarTextColor = *aColor; + aMenuBarRolloverTextColor = *aColor; + } + + aStyleSettings.SetMenuBarTextColor( aMenuBarTextColor ); + aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarRolloverTextColor ); + aStyleSettings.SetInactiveTabColor( aStyleSettings.GetFaceColor() ); aStyleSettings.SetLightColor( ImplWinColorToSal( GetSysColor( COLOR_3DHILIGHT ) ) ); aStyleSettings.SetLightBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DLIGHT ) ) ); @@ -2636,8 +2720,6 @@ void WinSalFrame::UpdateSettings( AllSettings& rSettings ) aStyleSettings.SetHelpColor( ImplWinColorToSal( GetSysColor( COLOR_INFOBK ) ) ); aStyleSettings.SetHelpTextColor( ImplWinColorToSal( GetSysColor( COLOR_INFOTEXT ) ) ); - Color aControlTextColor(ImplWinColorToSal(GetSysColor(COLOR_BTNTEXT))); - aStyleSettings.SetDialogColor(aStyleSettings.GetFaceColor()); aStyleSettings.SetDialogTextColor(aControlTextColor); @@ -2661,12 +2743,9 @@ void WinSalFrame::UpdateSettings( AllSettings& rSettings ) aStyleSettings.SetTabRolloverTextColor(aControlTextColor); aStyleSettings.SetTabHighlightTextColor(aControlTextColor); - aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); aStyleSettings.SetGroupTextColor( aStyleSettings.GetRadioCheckTextColor() ); aStyleSettings.SetLabelTextColor( aStyleSettings.GetRadioCheckTextColor() ); - aStyleSettings.SetWindowColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOW ) ) ); aStyleSettings.SetActiveTabColor( aStyleSettings.GetWindowColor() ); - aStyleSettings.SetWindowTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); aStyleSettings.SetToolTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); aStyleSettings.SetFieldColor( aStyleSettings.GetWindowColor() ); aStyleSettings.SetFieldTextColor( aStyleSettings.GetWindowTextColor() ); @@ -2692,17 +2771,6 @@ void WinSalFrame::UpdateSettings( AllSettings& rSettings ) aStyleSettings.SetMenuBorderColor( aStyleSettings.GetLightBorderColor() ); // overridden below for flat menus aStyleSettings.SetUseFlatBorders( false ); aStyleSettings.SetUseFlatMenus( false ); - aStyleSettings.SetMenuTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) ); - if ( std::optional<Color> aColor = aStyleSettings.GetPersonaMenuBarTextColor() ) - { - aStyleSettings.SetMenuBarTextColor( *aColor ); - aStyleSettings.SetMenuBarRolloverTextColor( *aColor ); - } - else - { - aStyleSettings.SetMenuBarTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) ); - aStyleSettings.SetMenuBarRolloverTextColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHTTEXT ) ) ); - } aStyleSettings.SetMenuBarHighlightTextColor(aStyleSettings.GetMenuHighlightTextColor()); aStyleSettings.SetActiveColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVECAPTION ) ) ); aStyleSettings.SetActiveTextColor( ImplWinColorToSal( GetSysColor( COLOR_CAPTIONTEXT ) ) ); @@ -4107,6 +4175,8 @@ static void ImplHandleSettingsChangeMsg( HWND hWnd, UINT nMsg, aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines(); else if( wParam == SPI_SETWHEELSCROLLCHARS ) aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars(); + UpdateDarkMode(hWnd); + GetSalData()->mbThemeChanged = true; } if ( WM_SYSCOLORCHANGE == nMsg && GetSalData()->mhDitherPal ) @@ -5499,6 +5569,9 @@ static LRESULT CALLBACK SalFrameWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LP if ( pFrame != nullptr ) { SetWindowPtr( hWnd, pFrame ); + + UpdateDarkMode(hWnd); + // Set HWND already here, as data might be used already // when messages are being sent by CreateWindow() pFrame->mhWnd = hWnd;