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;

Reply via email to