tdf#153229 add a switch to override honoring system dark mode

Change-Id: Iafb6182e05dc65d20d0809476ee58908f7426d39
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146597
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
(cherry picked from commit f7c03364e24da285ea95cea0cc688a7a120fc163)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146745
Reviewed-by: Sophie Gautier <sophi@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
(cherry picked from commit f0814dca8f05f524068b8217a9f1eabd8266cdc4)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147056
Reviewed-by: Christian Lohmaier <lohmaier+LibreOffice@googlemail.com>
Tested-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
diff --git a/cui/source/options/optgdlg.cxx b/cui/source/options/optgdlg.cxx
index 4405fdb..5663d39 100644
--- a/cui/source/options/optgdlg.cxx
+++ b/cui/source/options/optgdlg.cxx
@@ -528,6 +528,8 @@ OfaViewTabPage::OfaViewTabPage(weld::Container* pPage, weld::DialogController* p
    , m_xIconSizeLB(m_xBuilder->weld_combo_box("iconsize"))
    , m_xSidebarIconSizeLB(m_xBuilder->weld_combo_box("sidebariconsize"))
    , m_xNotebookbarIconSizeLB(m_xBuilder->weld_combo_box("notebookbariconsize"))
    , m_xDarkModeFrame(m_xBuilder->weld_widget("darkmode"))
    , m_xAppearanceStyleLB(m_xBuilder->weld_combo_box("appearance"))
    , m_xIconStyleLB(m_xBuilder->weld_combo_box("iconstyle"))
    , m_xFontAntiAliasing(m_xBuilder->weld_check_button("aafont"))
    , m_xAAPointLimitLabel(m_xBuilder->weld_label("aafrom"))
@@ -547,9 +549,14 @@ OfaViewTabPage::OfaViewTabPage(weld::Container* pPage, weld::DialogController* p
    , m_xMoreIcons(m_xBuilder->weld_button("btnMoreIcons"))
    , m_xRunGPTests(m_xBuilder->weld_button("btn_rungptest"))
{
    if (Application::GetToolkitName().startsWith("gtk"))
    OUString sToolKitName(Application::GetToolkitName());
    if (sToolKitName.startsWith("gtk"))
        m_xMenuIconBox->hide();

    const bool bHasDarkMode = sToolKitName.startsWith("gtk") || sToolKitName == "osx" || sToolKitName == "win";
    if (!bHasDarkMode)
        m_xDarkModeFrame->hide();

    m_xFontAntiAliasing->connect_toggled( LINK( this, OfaViewTabPage, OnAntialiasingToggled ) );

    m_xUseSkia->connect_toggled(LINK(this, OfaViewTabPage, OnUseSkiaToggled));
@@ -668,6 +675,7 @@ bool OfaViewTabPage::FillItemSet( SfxItemSet* )
{
    bool bModified = false;
    bool bMenuOptModified = false;
    bool bDarkModeOptModified = false;
    bool bRepaintWindows(false);
    std::shared_ptr<comphelper::ConfigurationChanges> xChanges(comphelper::ConfigurationChanges::create());

@@ -781,6 +789,12 @@ bool OfaViewTabPage::FillItemSet( SfxItemSet* )
        bAppearanceChanged = true;
    }

    if (m_xAppearanceStyleLB->get_value_changed_from_saved())
    {
        bDarkModeOptModified = true;
        bModified = true;
    }

    if (m_xContextMenuShortcutsLB->get_value_changed_from_saved())
    {
        officecfg::Office::Common::View::Menu::ShortcutsInContextMenus::set(
@@ -824,12 +838,20 @@ bool OfaViewTabPage::FillItemSet( SfxItemSet* )

    xChanges->commit();

    if( bMenuOptModified )
    if (bMenuOptModified || bDarkModeOptModified)
    {
        // Set changed settings to the application instance
        AllSettings aAllSettings = Application::GetSettings();
        StyleSettings aStyleSettings = aAllSettings.GetStyleSettings();
        aAllSettings.SetStyleSettings(aStyleSettings);

        if (bMenuOptModified)
        {
            StyleSettings aStyleSettings = aAllSettings.GetStyleSettings();
            aAllSettings.SetStyleSettings(aStyleSettings);
        }

        if (bDarkModeOptModified)
            MiscSettings::SetDarkMode(m_xAppearanceStyleLB->get_active());

        Application::MergeSystemSettings( aAllSettings );
        Application::SetSettings(aAllSettings);
    }
@@ -912,6 +934,9 @@ void OfaViewTabPage::Reset( const SfxItemSet* )
    m_xIconStyleLB->set_active(nStyleLB_InitialSelection);
    m_xIconStyleLB->save_value();

    m_xAppearanceStyleLB->set_active(officecfg::Office::Common::Misc::Appearance::get());
    m_xAppearanceStyleLB->save_value();

    // Mouse Snap
    m_xMousePosLB->set_active(static_cast<sal_Int32>(pAppearanceCfg->GetSnapMode()));
    m_xMousePosLB->save_value();
diff --git a/cui/source/options/optgdlg.hxx b/cui/source/options/optgdlg.hxx
index a4c686c..870e3f3 100644
--- a/cui/source/options/optgdlg.hxx
+++ b/cui/source/options/optgdlg.hxx
@@ -87,6 +87,8 @@ private:
    std::unique_ptr<weld::ComboBox> m_xIconSizeLB;
    std::unique_ptr<weld::ComboBox> m_xSidebarIconSizeLB;
    std::unique_ptr<weld::ComboBox> m_xNotebookbarIconSizeLB;
    std::unique_ptr<weld::Widget> m_xDarkModeFrame;
    std::unique_ptr<weld::ComboBox> m_xAppearanceStyleLB;
    std::unique_ptr<weld::ComboBox> m_xIconStyleLB;

    std::unique_ptr<weld::CheckButton> m_xFontAntiAliasing;
diff --git a/cui/uiconfig/ui/optviewpage.ui b/cui/uiconfig/ui/optviewpage.ui
index ac6b7fc..a371f83 100644
--- a/cui/uiconfig/ui/optviewpage.ui
+++ b/cui/uiconfig/ui/optviewpage.ui
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<!-- Generated with glade 3.40.0 -->
<interface domain="cui">
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkAdjustment" id="adjustment2">
@@ -18,7 +18,7 @@
    <property name="border-width">6</property>
    <property name="column-spacing">24</property>
    <child>
      <!-- n-columns=1 n-rows=4 -->
      <!-- n-columns=1 n-rows=5 -->
      <object class="GtkGrid" id="grid2">
        <property name="visible">True</property>
        <property name="can-focus">False</property>
@@ -126,7 +126,7 @@
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">3</property>
            <property name="top-attach">4</property>
          </packing>
        </child>
        <child>
@@ -249,7 +249,7 @@
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">2</property>
            <property name="top-attach">3</property>
          </packing>
        </child>
        <child>
@@ -390,7 +390,7 @@
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">1</property>
            <property name="top-attach">2</property>
          </packing>
        </child>
        <child>
@@ -474,6 +474,76 @@
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkFrame" id="darkmode">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="hexpand">True</property>
            <property name="label-xalign">0</property>
            <property name="shadow-type">none</property>
            <child>
              <!-- n-columns=2 n-rows=1 -->
              <object class="GtkGrid" id="refgrid2">
                <property name="visible">True</property>
                <property name="can-focus">False</property>
                <property name="margin-start">12</property>
                <property name="margin-top">6</property>
                <property name="hexpand">True</property>
                <property name="column-spacing">6</property>
                <child>
                  <object class="GtkComboBoxText" id="appearance">
                    <property name="visible">True</property>
                    <property name="can-focus">False</property>
                    <property name="hexpand">True</property>
                    <property name="active">0</property>
                    <items>
                      <item translatable="yes" context="optviewpage|appearance">System</item>
                      <item translatable="yes" context="optviewpage|appearance">Light</item>
                      <item translatable="yes" context="optviewpage|appearance">Dark</item>
                    </items>
                    <child internal-child="accessible">
                      <object class="AtkObject" id="appearance-atkobject">
                        <property name="AtkObject::accessible-description" translatable="yes" context="extended_tip | appearance">Specifies whether to follow the system appearance mode or override Dark or Light.</property>
                      </object>
                    </child>
                  </object>
                  <packing>
                    <property name="left-attach">1</property>
                    <property name="top-attach">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkLabel" id="label7">
                    <property name="visible">True</property>
                    <property name="can-focus">False</property>
                    <property name="label" translatable="yes" context="optviewpage|label7">Mode:</property>
                    <property name="use-underline">True</property>
                    <property name="mnemonic-widget">appearance</property>
                    <property name="xalign">0</property>
                  </object>
                  <packing>
                    <property name="left-attach">0</property>
                    <property name="top-attach">0</property>
                  </packing>
                </child>
              </object>
            </child>
            <child type="label">
              <object class="GtkLabel" id="label16">
                <property name="visible">True</property>
                <property name="can-focus">False</property>
                <property name="label" translatable="yes" context="optviewpage|label16">Appearance</property>
                <attributes>
                  <attribute name="weight" value="bold"/>
                </attributes>
              </object>
            </child>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">0</property>
          </packing>
        </child>
@@ -757,26 +827,28 @@
  </object>
  <object class="GtkSizeGroup" id="sizegroupLabel">
    <widgets>
      <widget name="label6"/>
      <widget name="label14"/>
      <widget name="label8"/>
      <widget name="label9"/>
      <widget name="label13"/>
      <widget name="label10"/>
      <widget name="label11"/>
      <widget name="label12"/>
      <widget name="label13"/>
      <widget name="label10"/>
      <widget name="label8"/>
      <widget name="label9"/>
      <widget name="label14"/>
      <widget name="label6"/>
      <widget name="label7"/>
    </widgets>
  </object>
  <object class="GtkSizeGroup" id="sizegroupWidget">
    <widgets>
      <widget name="iconstyle"/>
      <widget name="iconsize"/>
      <widget name="notebookbariconsize"/>
      <widget name="sidebariconsize"/>
      <widget name="menuicons"/>
      <widget name="contextmenushortcuts"/>
      <widget name="mousepos"/>
      <widget name="mousemiddle"/>
      <widget name="menuicons"/>
      <widget name="contextmenushortcuts"/>
      <widget name="notebookbariconsize"/>
      <widget name="sidebariconsize"/>
      <widget name="iconsize"/>
      <widget name="iconstyle"/>
      <widget name="appearance"/>
    </widgets>
  </object>
</interface>
diff --git a/include/vcl/settings.hxx b/include/vcl/settings.hxx
index 9d86554..de6a4b9 100644
--- a/include/vcl/settings.hxx
+++ b/include/vcl/settings.hxx
@@ -645,6 +645,9 @@ public:
    bool                            GetDisablePrinting() const;
    void                            SetEnableLocalizedDecimalSep( bool bEnable );
    bool                            GetEnableLocalizedDecimalSep() const;
    // 0 auto, 1 light, 2, dark
    static void                     SetDarkMode(int nMode);
    static int                      GetDarkMode();

    bool                            operator ==( const MiscSettings& rSet ) const;
    bool                            operator !=( const MiscSettings& rSet ) const;
diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs
index ea175aa..24716e1 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs
@@ -5416,6 +5416,31 @@
      <info>
        <desc>Determines the miscellaneous entries for the common group.</desc>
      </info>
      <prop oor:name="Appearance" oor:type="xs:short" oor:nillable="false">
        <!-- UIHints: Tools  Options - General  View  [Section] Appearance -->
        <info>
          <desc>Specifies the appearance of the user interface.</desc>
          <label>Appearance</label>
        </info>
        <constraints>
          <enumeration oor:value="0">
            <info>
              <desc>Automatic, from system settings</desc>
            </info>
          </enumeration>
          <enumeration oor:value="1">
            <info>
              <desc>Light</desc>
            </info>
          </enumeration>
          <enumeration oor:value="2">
            <info>
              <desc>Dark</desc>
            </info>
          </enumeration>
        </constraints>
        <value>0</value>
      </prop>
      <prop oor:name="MaxOpenDocuments" oor:type="xs:int">
        <info>
          <desc>Determines the maximum count of documents, which are allowed to
diff --git a/vcl/inc/osx/salframe.h b/vcl/inc/osx/salframe.h
index 71b8eb4..af8783b 100644
--- a/vcl/inc/osx/salframe.h
+++ b/vcl/inc/osx/salframe.h
@@ -163,6 +163,7 @@ public:
        tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override;
    // done setting up the clipregion
    virtual void EndSetClipRegion() override;
    virtual void UpdateDarkMode() override;

    void UpdateFrameGeometry();

diff --git a/vcl/inc/salframe.hxx b/vcl/inc/salframe.hxx
index 552d88e..a78f9af 100644
--- a/vcl/inc/salframe.hxx
+++ b/vcl/inc/salframe.hxx
@@ -299,6 +299,8 @@ public:
    void SetModalHierarchyHdl(const Link<bool, void>& rLink) { m_aModalHierarchyHdl = rLink; }
    void NotifyModalHierarchy(bool bModal) { m_aModalHierarchyHdl.Call(bModal); }

    virtual void            UpdateDarkMode() {}

    // Call the callback set; this sometimes necessary for implementation classes
    // that should not know more than necessary about the SalFrame implementation
    // (e.g. input methods, printer update handlers).
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 5b2df74..bd5f4de 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -611,6 +611,7 @@ public:
    virtual bool                UpdatePopover(void* nId, const OUString& rHelpText, vcl::Window* pParent, const tools::Rectangle& rHelpArea) override;
    virtual bool                HidePopover(void* nId) override;
    virtual weld::Window*       GetFrameWeld() const override;
    virtual void                UpdateDarkMode() override;

    static GtkSalFrame         *getFromWindow( GtkWidget *pWindow );

diff --git a/vcl/inc/win/salframe.h b/vcl/inc/win/salframe.h
index dedac69..78f6194 100644
--- a/vcl/inc/win/salframe.h
+++ b/vcl/inc/win/salframe.h
@@ -139,6 +139,7 @@ public:
    virtual void                BeginSetClipRegion( sal_uInt32 nRects ) override;
    virtual void                UnionClipRegion( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override;
    virtual void                EndSetClipRegion() override;
    virtual void                UpdateDarkMode() override;

    constexpr vcl::WindowState state() const { return m_eState; }
    void UpdateFrameState();
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
index 02a3879..bf2257b 100644
--- a/vcl/osx/salframe.cxx
+++ b/vcl/osx/salframe.cxx
@@ -1258,6 +1258,26 @@ void AquaSalFrame::getResolution( sal_Int32& o_rDPIX, sal_Int32& o_rDPIY )
    mpGraphics->GetResolution( o_rDPIX, o_rDPIY );
}

void AquaSalFrame::UpdateDarkMode()
{
    if (@available(macOS 10.14, iOS 13, *))
    {
        switch (MiscSettings::GetDarkMode())
        {
            case 0: // auto
            default:
                [NSApp setAppearance: nil];
                break;
            case 1: // light
                [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]];
                break;
            case 2: // dark
                [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua]];
                break;
        }
    }
}

// on OSX-Aqua the style settings are independent of the frame, so it does
// not really belong here. Since the connection to the Appearance_Manager
// is currently done in salnativewidgets.cxx this would be a good place.
@@ -1289,11 +1309,11 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
    StyleSettings aStyleSettings = rSettings.GetStyleSettings();

    bool bUseDarkMode(false);
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    if (userDefaults != nil)
    if (@available(macOS 10.14, iOS 13, *))
    {
        NSString* setting = [userDefaults stringForKey: @"AppleInterfaceStyle"];
        bUseDarkMode = (setting && [setting isEqual: @"Dark"]);
        NSAppearanceName match = [mpNSView.effectiveAppearance bestMatchFromAppearancesWithNames: @[
                                  NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
        bUseDarkMode = [match isEqualToString: NSAppearanceNameDarkAqua];
    }
    // there is no sukapura_dark, at the time of writing at least, so whatever
    // is considered the default dark icon set will be used
diff --git a/vcl/source/app/settings.cxx b/vcl/source/app/settings.cxx
index c12a520..9762c21 100644
--- a/vcl/source/app/settings.cxx
+++ b/vcl/source/app/settings.cxx
@@ -53,6 +53,7 @@

using namespace ::com::sun::star;

#include <salframe.hxx>
#include <svdata.hxx>

struct ImplMouseData
@@ -2755,6 +2756,25 @@ bool MiscSettings::GetEnableLocalizedDecimalSep() const
    return mxData->mbEnableLocalizedDecimalSep;
}

int MiscSettings::GetDarkMode()
{
    return officecfg::Office::Common::Misc::Appearance::get();
}

void MiscSettings::SetDarkMode(int nMode)
{
    std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
    officecfg::Office::Common::Misc::Appearance::set(nMode, batch);
    batch->commit();

    vcl::Window *pWin = Application::GetFirstTopLevelWindow();
    while (pWin)
    {
        pWin->ImplGetFrame()->UpdateDarkMode();
        pWin = Application::GetNextTopLevelWindow(pWin);
    }
}

HelpSettings::HelpSettings()
    : mxData(std::make_shared<ImplHelpData>())
{
diff --git a/vcl/unx/gtk3/gtkframe.cxx b/vcl/unx/gtk3/gtkframe.cxx
index 1d72e46..69dc11b 100644
--- a/vcl/unx/gtk3/gtkframe.cxx
+++ b/vcl/unx/gtk3/gtkframe.cxx
@@ -45,6 +45,7 @@
#include <window.h>

#include <basegfx/vector/b2ivector.hxx>
#include <officecfg/Office/Common.hxx>

#include <dlfcn.h>

@@ -1315,20 +1316,20 @@ namespace
        PREFER_LIGHT
    };

    bool ReadColorScheme(GDBusProxy* proxy, GVariant** out)
    void ReadColorScheme(GDBusProxy* proxy, GVariant** out)
    {
        g_autoptr (GVariant) ret =
            g_dbus_proxy_call_sync(proxy, "Read",
                                   g_variant_new ("(ss)", "org.freedesktop.appearance", "color-scheme"),
                                   G_DBUS_CALL_FLAGS_NONE, G_MAXINT, nullptr, nullptr);
        if (!ret)
            return false;
            return;

        g_autoptr (GVariant) child = nullptr;
        g_variant_get(ret, "(v)", &child);
        g_variant_get(child, "v", out);

        return true;
        return;
    }
}

@@ -1337,9 +1338,30 @@ void GtkSalFrame::SetColorScheme(GVariant* variant)
    if (!m_pWindow)
        return;

    guint32 color_scheme = g_variant_get_uint32(variant);
    if (color_scheme > PREFER_LIGHT)
        color_scheme = DEFAULT;
    guint32 color_scheme;

    switch (officecfg::Office::Common::Misc::Appearance::get())
    {
        default:
        case 0: // Auto
        {
            if (variant)
            {
                color_scheme = g_variant_get_uint32(variant);
                if (color_scheme > PREFER_LIGHT)
                    color_scheme = DEFAULT;
            }
            else
                color_scheme = DEFAULT;
            break;
        }
        case 1: // Light
            color_scheme = PREFER_LIGHT;
            break;
        case 2: // Dark
            color_scheme = PREFER_DARK;
            break;
    }

    bool bDarkIconTheme(color_scheme == PREFER_DARK);
    GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow);
@@ -1380,18 +1402,20 @@ void GtkSalFrame::ListenPortalSettings()
                                              "org.freedesktop.portal.Settings",
                                              nullptr,
                                              nullptr);
    if (!m_pSettingsPortal)
        return;

    g_autoptr (GVariant) value = nullptr;
    UpdateDarkMode();

    if (!ReadColorScheme(m_pSettingsPortal, &value))
        return;

    SetColorScheme(value);
    m_nPortalSettingChangedSignalId = g_signal_connect(m_pSettingsPortal, "g-signal", G_CALLBACK(settings_portal_changed_cb), this);
}

void GtkSalFrame::UpdateDarkMode()
{
    g_autoptr (GVariant) value = nullptr;
    if (m_pSettingsPortal)
        ReadColorScheme(m_pSettingsPortal, &value);
    SetColorScheme(value);
}

#if GTK_CHECK_VERSION(4,0,0)
static void PopoverClosed(GtkPopover*, GtkSalFrame* pThis)
{
diff --git a/vcl/win/gdi/salnativewidgets-luna.cxx b/vcl/win/gdi/salnativewidgets-luna.cxx
index 1cd6245..c837cb7 100644
--- a/vcl/win/gdi/salnativewidgets-luna.cxx
+++ b/vcl/win/gdi/salnativewidgets-luna.cxx
@@ -402,16 +402,30 @@ bool UseDarkMode()
    if (!bOSSupportsDarkMode)
        return false;

    HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
    if (!hUxthemeLib)
        return false;
    bool bRet(false);
    switch (MiscSettings::GetDarkMode())
    {
        case 0: // auto
        default:
        {
            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();
            typedef bool(WINAPI* ShouldAppsUseDarkMode_t)();
            if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132))))
                bRet = ShouldAppsUseDarkMode();

    FreeLibrary(hUxthemeLib);
            FreeLibrary(hUxthemeLib);
            break;
        }
        case 1: // light
            bRet = false;
            break;
        case 2: // dark
            bRet = true;
            break;
    }

    return bRet;
}
@@ -421,7 +435,8 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
                            ControlPart nPart,
                            ControlState nState,
                            const ImplControlValue& aValue,
                            OUString const & aCaption )
                            OUString const & aCaption,
                            bool bUseDarkMode )
{
    // a listbox dropdown is actually a combobox dropdown
    if( nType == ControlType::Listbox )
@@ -767,7 +782,7 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
    {
        // tabpane in tabcontrols gets drawn in "darkmode" as if it was a
        // a "light" theme, so bodge this by drawing a frame directly
        if (UseDarkMode())
        if (bUseDarkMode)
        {
            Color aColor(Application::GetSettings().GetStyleSettings().GetDisableColor());
            ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
@@ -783,7 +798,7 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
    if( nType == ControlType::TabBody )
    {
        // tabbody in main window gets drawn in white in "darkmode", so bodge this here
        if (UseDarkMode())
        if (bUseDarkMode)
        {
            Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
            ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
@@ -843,7 +858,7 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,

        // tabitem in tabcontrols gets drawn in "darkmode" as if it was a
        // a "light" theme, so bodge this by drawing with a button instead
        if (UseDarkMode())
        if (bUseDarkMode)
        {
            Color aColor;
            if (iState == TILES_SELECTED)
@@ -911,7 +926,7 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
            }

            // toolbar in main window gets drawn in white in "darkmode", so bodge this here
            if (UseDarkMode())
            if (bUseDarkMode)
            {
                Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
                ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
@@ -943,7 +958,7 @@ static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
                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())
                if (bUseDarkMode)
                {
                    Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
                    ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
@@ -1302,7 +1317,7 @@ bool WinSalGraphics::drawNativeControl( ControlType nType,
        // set default text alignment
        int ta = SetTextAlign(getHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);

        bOk = ImplDrawNativeControl(getHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr);
        bOk = ImplDrawNativeControl(getHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode);

        // restore alignment
        SetTextAlign(getHDC(), ta);
@@ -1318,8 +1333,8 @@ bool WinSalGraphics::drawNativeControl( ControlType nType,
        SetTextAlign(aWhiteDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
        aWhiteDC->fill(RGB(0xff, 0xff, 0xff));

        if (ImplDrawNativeControl(aBlackDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr) &&
            ImplDrawNativeControl(aWhiteDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr))
        if (ImplDrawNativeControl(aBlackDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode) &&
            ImplDrawNativeControl(aWhiteDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode))
        {
            bOk = pImpl->RenderAndCacheNativeControl(*aWhiteDC, *aBlackDC, cacheRect.Left(), cacheRect.Top(), aControlCacheKey);
        }
diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx
index dc804a7..73842a7 100644
--- a/vcl/win/window/salframe.cxx
+++ b/vcl/win/window/salframe.cxx
@@ -261,6 +261,17 @@ void ImplSalGetWorkArea( HWND hWnd, RECT *pRect, const RECT *pParentRect )
    }
}

namespace {

enum PreferredAppMode
{
    AllowDark = 1,
    ForceDark = 2,
    ForceLight = 3
};

}

static void UpdateDarkMode(HWND hWnd)
{
    static bool bOSSupportsDarkMode = OSSupportsDarkMode();
@@ -271,18 +282,37 @@ static void UpdateDarkMode(HWND hWnd)
    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))))
    typedef PreferredAppMode(WINAPI* SetPreferredAppMode_t)(PreferredAppMode);
    auto SetPreferredAppMode = reinterpret_cast<SetPreferredAppMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(135)));
    if (SetPreferredAppMode)
    {
        BOOL bDarkMode = ShouldAppsUseDarkMode();
        DwmSetWindowAttribute(hWnd, 20, &bDarkMode, sizeof(bDarkMode));
        switch (MiscSettings::GetDarkMode())
        {
            case 0:
                SetPreferredAppMode(AllowDark);
                break;
            case 1:
                SetPreferredAppMode(ForceLight);
                break;
            case 2:
                SetPreferredAppMode(ForceDark);
                break;
        }
    }

    BOOL bDarkMode = UseDarkMode();

    typedef void(WINAPI* AllowDarkModeForWindow_t)(HWND, BOOL);
    auto AllowDarkModeForWindow = reinterpret_cast<AllowDarkModeForWindow_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(133)));
    if (AllowDarkModeForWindow)
        AllowDarkModeForWindow(hWnd, bDarkMode);

    FreeLibrary(hUxthemeLib);

    if (!AllowDarkModeForWindow)
        return;

    DwmSetWindowAttribute(hWnd, 20, &bDarkMode, sizeof(bDarkMode));
}

SalFrame* ImplSalCreateFrame( WinSalInstance* pInst,
@@ -3061,6 +3091,11 @@ void WinSalFrame::EndSetClipRegion()
    }
}

void WinSalFrame::UpdateDarkMode()
{
    ::UpdateDarkMode(mhWnd);
}

static bool ImplHandleMouseMsg( HWND hWnd, UINT nMsg,
                                WPARAM wParam, LPARAM lParam )
{