chromium/ui/native_theme/native_theme_win.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_NATIVE_THEME_NATIVE_THEME_WIN_H_
#define UI_NATIVE_THEME_NATIVE_THEME_WIN_H_

// A wrapper class for working with custom XP/Vista themes provided in
// uxtheme.dll.  This is a singleton class that can be grabbed using
// NativeThemeWin::instance().
// For more information on visual style parts and states, see:
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
#include <windows.h>

#include <optional>

#include "base/no_destructor.h"
#include "base/win/registry.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/sys_color_change_listener.h"
#include "ui/native_theme/native_theme.h"

class SkCanvas;

namespace ui {

// Windows implementation of native theme class.
//
// At the moment, this class in in transition from an older API that consists
// of several PaintXXX methods to an API, inherited from the NativeTheme base
// class, that consists of a single Paint() method with a argument to indicate
// what kind of part to paint.
class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme,
                                           public gfx::SysColorChangeListener {
 public:
  enum ThemeName {
    BUTTON,
    LIST,
    MENU,
    MENULIST,
    SCROLLBAR,
    STATUS,
    TAB,
    TEXTFIELD,
    TRACKBAR,
    WINDOW,
    PROGRESS,
    SPIN,
    LAST
  };

  NativeThemeWin(const NativeThemeWin&) = delete;
  NativeThemeWin& operator=(const NativeThemeWin&) = delete;

  // Closes cached theme handles so we can unload the DLL or update our UI
  // for a theme change.
  static void CloseHandles();

  // NativeTheme implementation:
  gfx::Size GetPartSize(Part part,
                        State state,
                        const ExtraParams& extra) const override;
  void Paint(cc::PaintCanvas* canvas,
             const ui::ColorProvider* color_provider,
             Part part,
             State state,
             const gfx::Rect& rect,
             const ExtraParams& extra,
             ColorScheme color_scheme,
             bool in_forced_colors,
             const std::optional<SkColor>& accent_color) const override;
  bool SupportsNinePatch(Part part) const override;
  gfx::Size GetNinePatchCanvasSize(Part part) const override;
  gfx::Rect GetNinePatchAperture(Part part) const override;
  bool ShouldUseDarkColors() const override;

  // On Windows, we look at the high contrast setting to calculate the color
  // scheme. If high contrast is enabled, the preferred color scheme calculation
  // will ignore the state of dark mode. Instead, preferred color scheme will be
  // light or dark depending on the OS high contrast theme. If high contrast is
  // off, the preferred color scheme calculation will be based of the state of
  // dark mode.
  PreferredColorScheme CalculatePreferredColorScheme() const override;

  PreferredContrast CalculatePreferredContrast() const override;
  ColorScheme GetDefaultSystemColorScheme() const override;

 protected:
  friend class NativeTheme;
  friend class base::NoDestructor<NativeThemeWin>;

  // NativeTheme:
  void ConfigureWebInstance() override;
  std::optional<base::TimeDelta> GetPlatformCaretBlinkInterval() const override;

  NativeThemeWin(bool configure_web_instance, bool should_only_use_dark_colors);
  ~NativeThemeWin() override;

 private:
  bool IsUsingHighContrastThemeInternal() const;
  void CloseHandlesInternal();

  // gfx::SysColorChangeListener:
  void OnSysColorChange() override;

  // Update the locally cached set of system colors.
  void UpdateSystemColors();

  // Painting functions that paint to PaintCanvas.
  void PaintMenuSeparator(cc::PaintCanvas* canvas,
                          const ColorProvider* color_provider,
                          const MenuSeparatorExtraParams& params) const;
  void PaintMenuGutter(cc::PaintCanvas* canvas,
                       const ColorProvider* color_provider,
                       const gfx::Rect& rect) const;
  void PaintMenuBackground(cc::PaintCanvas* canvas,
                           const ColorProvider* color_provider,
                           const gfx::Rect& rect) const;

  // Paint directly to canvas' HDC.
  void PaintDirect(SkCanvas* destination_canvas,
                   HDC hdc,
                   Part part,
                   State state,
                   const gfx::Rect& rect,
                   const ExtraParams& extra) const;

  // Create a temporary HDC, paint to that, clean up the alpha values in the
  // temporary HDC, and then blit the result to canvas.  This is to work around
  // the fact that Windows XP and some classic themes give bogus alpha values.
  void PaintIndirect(cc::PaintCanvas* destination_canvas,
                     Part part,
                     State state,
                     const gfx::Rect& rect,
                     const ExtraParams& extra) const;

  // Various helpers to paint specific parts.
  void PaintButtonClassic(HDC hdc,
                          Part part,
                          State state,
                          RECT* rect,
                          const ButtonExtraParams& extra) const;
  void PaintLeftMenuArrowThemed(HDC hdc,
                                HANDLE handle,
                                int part_id,
                                int state_id,
                                const gfx::Rect& rect) const;
  void PaintScrollbarArrowClassic(HDC hdc,
                                  Part part,
                                  State state,
                                  RECT* rect) const;
  void PaintScrollbarTrackClassic(SkCanvas* canvas,
                                  HDC hdc,
                                  RECT* rect,
                                  const ScrollbarTrackExtraParams& extra) const;
  void PaintHorizontalTrackbarThumbClassic(
      SkCanvas* canvas,
      HDC hdc,
      const RECT& rect,
      const TrackbarExtraParams& extra) const;
  void PaintProgressBarOverlayThemed(HDC hdc,
                                     HANDLE handle,
                                     RECT* bar_rect,
                                     RECT* value_rect,
                                     const ProgressBarExtraParams& extra) const;
  void PaintTextFieldThemed(HDC hdc,
                            HANDLE handle,
                            HBRUSH bg_brush,
                            int part_id,
                            int state_id,
                            RECT* rect,
                            const TextFieldExtraParams& extra) const;
  void PaintTextFieldClassic(HDC hdc,
                             HBRUSH bg_brush,
                             RECT* rect,
                             const TextFieldExtraParams& extra) const;

  // Paints a theme part, with support for scene scaling in high-DPI mode.
  // |theme| is the theme handle. |hdc| is the handle for the device context.
  // |part_id| is the identifier for the part (e.g. thumb gripper). |state_id|
  // is the identifier for the rendering state of the part (e.g. hover). |rect|
  // is the bounds for rendering, expressed in logical coordinates.
  void PaintScaledTheme(HANDLE theme,
                        HDC hdc,
                        int part_id,
                        int state_id,
                        const gfx::Rect& rect) const;

  // Get the windows theme name/part/state.  These three helper functions are
  // used only by GetPartSize(), as each of the corresponding PaintXXX()
  // methods do further validation of the part and state that is required for
  // getting the size.
  static ThemeName GetThemeName(Part part);
  static int GetWindowsPart(Part part, State state, const ExtraParams& extra);
  static int GetWindowsState(Part part, State state, const ExtraParams& extra);

  HRESULT PaintFrameControl(HDC hdc,
                            const gfx::Rect& rect,
                            UINT type,
                            UINT state,
                            bool is_selected,
                            State control_state) const;

  // Returns a handle to the theme data.
  HANDLE GetThemeHandle(ThemeName theme_name) const;

  void RegisterThemeRegkeyObserver();
  void RegisterColorFilteringRegkeyObserver();
  void UpdateDarkModeStatus();
  void UpdatePrefersReducedTransparency();
  void UpdateInvertedColors();

  // True if Windows supports dark mode. This does NOT indicate whether the
  // system is in dark mode, only that it is supported by this version of
  // Windows.
  const bool supports_windows_dark_mode_;

  // Dark Mode/Transparency registry key.
  base::win::RegKey hkcu_themes_regkey_;

  // Inverted colors registry key
  base::win::RegKey hkcu_color_filtering_regkey_;

  // A cache of open theme handles.
  mutable HANDLE theme_handles_[LAST];

  // The system color change listener and the updated cache of system colors.
  gfx::ScopedSysColorChangeListener color_change_listener_;

  // Used to notify the web native theme of changes to dark mode, high
  // contrast, preferred color scheme, and preferred contrast.
  std::unique_ptr<NativeTheme::ColorSchemeNativeThemeObserver>
      color_scheme_observer_;
};

}  // namespace ui

#endif  // UI_NATIVE_THEME_NATIVE_THEME_WIN_H_