1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef UI_NATIVE_THEME_NATIVE_THEME_H_
6 #define UI_NATIVE_THEME_NATIVE_THEME_H_
7 
8 #include <map>
9 
10 #include "base/containers/flat_map.h"
11 #include "base/macros.h"
12 #include "base/observer_list.h"
13 #include "build/build_config.h"
14 #include "cc/paint/paint_canvas.h"
15 #include "third_party/skia/include/core/SkColor.h"
16 #include "ui/base/models/menu_separator_types.h"
17 #include "ui/gfx/geometry/rect.h"
18 #include "ui/gfx/geometry/size.h"
19 #include "ui/gfx/native_widget_types.h"
20 #include "ui/native_theme/caption_style.h"
21 #include "ui/native_theme/native_theme_color_id.h"
22 #include "ui/native_theme/native_theme_export.h"
23 #include "ui/native_theme/native_theme_observer.h"
24 
25 namespace gfx {
26 class Rect;
27 class Size;
28 }
29 
30 namespace ui {
31 
32 // This class supports drawing UI controls (like buttons, text fields, lists,
33 // comboboxes, etc) that look like the native UI controls of the underlying
34 // platform, such as Windows or Linux. It also supplies default colors for
35 // dialog box backgrounds, etc., which are obtained from the system theme where
36 // possible.
37 //
38 // The supported control types are listed in the Part enum.  These parts can be
39 // in any state given by the State enum, where the actual definition of the
40 // state is part-specific. The supported colors are listed in the ColorId enum.
41 //
42 // Some parts require more information than simply the state in order to be
43 // drawn correctly, and this information is given to the Paint() method via the
44 // ExtraParams union.  Each part that requires more information has its own
45 // field in the union.
46 //
47 // NativeTheme also supports getting the default size of a given part with
48 // the GetPartSize() method.
49 class NATIVE_THEME_EXPORT NativeTheme {
50  public:
51   // The part to be painted / sized.
52   enum Part {
53     kCheckbox,
54 #if (defined(OS_LINUX) || defined(OS_BSD)) && !defined(OS_CHROMEOS)
55     kFrameTopArea,
56 #endif
57     kInnerSpinButton,
58     kMenuList,
59     kMenuPopupBackground,
60 #if defined(OS_WIN)
61     kMenuCheck,
62     kMenuCheckBackground,
63     kMenuPopupArrow,
64     kMenuPopupGutter,
65 #endif
66     kMenuPopupSeparator,
67     kMenuItemBackground,
68     kProgressBar,
69     kPushButton,
70     kRadio,
71 
72     // The order of the arrow enums is important, do not change without also
73     // changing the code in platform implementations.
74     kScrollbarDownArrow,
75     kScrollbarLeftArrow,
76     kScrollbarRightArrow,
77     kScrollbarUpArrow,
78 
79     kScrollbarHorizontalThumb,
80     kScrollbarVerticalThumb,
81     kScrollbarHorizontalTrack,
82     kScrollbarVerticalTrack,
83     kScrollbarHorizontalGripper,
84     kScrollbarVerticalGripper,
85     // The corner is drawn when there is both a horizontal and vertical
86     // scrollbar.
87     kScrollbarCorner,
88     kSliderTrack,
89     kSliderThumb,
90     kTabPanelBackground,
91     kTextField,
92     kTrackbarThumb,
93     kTrackbarTrack,
94     kWindowResizeGripper,
95     kMaxPart,
96   };
97 
98   // The state of the part.
99   enum State {
100     // IDs defined as specific values for use in arrays.
101     kDisabled = 0,
102     kHovered  = 1,
103     kNormal   = 2,
104     kPressed  = 3,
105     kNumStates = kPressed + 1,
106   };
107 
108   // OS-level preferred color scheme. (Ex. high contrast or dark mode color
109   // preference.)
110   enum class PreferredColorScheme {
111     kDark = 0,
112     kLight = 1,
113     kMaxValue = kLight,
114   };
115 
116   // OS-level preferred contrast. (Ex. high contrast or increased contrast.)
117   enum class PreferredContrast {
118     kNoPreference = 0,
119     kMore = 1,
120     kLess = 2,
121     kMaxValue = kLess,
122   };
123 
124   // IMPORTANT!
125   // This enum is reporting in metrics. Do not reorder; add additional values at
126   // the end.
127   //
128   // This represents the OS-level high contrast theme. kNone unless the default
129   // system color scheme is kPlatformHighContrast.
130   enum class PlatformHighContrastColorScheme {
131     kNone = 0,
132     kDark = 1,
133     kLight = 2,
134     kMaxValue = kLight,
135   };
136 
137   // The color scheme used for painting the native controls.
138   enum class ColorScheme {
139     kDefault,
140     kLight,
141     kDark,
142     kPlatformHighContrast,  // When the platform is providing HC colors (eg.
143                             // Win)
144   };
145 
146   // This enum represents the available unique security chip color states.
147   enum class SecurityChipColorId {
148     DEFAULT,
149     SECURE,
150     SECURE_WITH_CERT,
151     DANGEROUS,
152   };
153 
154   // Each structure below holds extra information needed when painting a given
155   // part.
156 
157   struct ButtonExtraParams {
158     bool checked;
159     bool indeterminate;  // Whether the button state is indeterminate.
160     bool is_default;  // Whether the button is default button.
161     bool is_focused;
162     bool has_border;
163     int classic_state;  // Used on Windows when uxtheme is not available.
164     SkColor background_color;
165     float zoom;
166   };
167 
168   struct FrameTopAreaExtraParams {
169     // Distinguishes between active (foreground) and inactive
170     // (background) window frame styles.
171     bool is_active;
172     bool incognito;
173     // True when Chromium renders the titlebar.  False when the window
174     // manager renders the titlebar.
175     bool use_custom_frame;
176     // If the NativeTheme will paint a solid color, it should use
177     // |default_background_color|.
178     SkColor default_background_color;
179   };
180 
181   struct InnerSpinButtonExtraParams {
182     bool spin_up;
183     bool read_only;
184     int classic_state;  // Used on Windows when uxtheme is not available.
185   };
186 
187   struct MenuArrowExtraParams {
188     bool pointing_right;
189     // Used for the disabled state to indicate if the item is both disabled and
190     // selected.
191     bool is_selected;
192   };
193 
194   struct MenuCheckExtraParams {
195     bool is_radio;
196     // Used for the disabled state to indicate if the item is both disabled and
197     // selected.
198     bool is_selected;
199   };
200 
201   struct MenuSeparatorExtraParams {
202     const gfx::Rect* paint_rect;
203     MenuSeparatorType type;
204   };
205 
206   struct MenuItemExtraParams {
207     bool is_selected;
208     int corner_radius;
209   };
210 
211   struct MenuListExtraParams {
212     bool has_border;
213     bool has_border_radius;
214     int arrow_x;
215     int arrow_y;
216     int arrow_size;
217     SkColor arrow_color;
218     SkColor background_color;
219     int classic_state;  // Used on Windows when uxtheme is not available.
220   };
221 
222   struct MenuBackgroundExtraParams {
223     int corner_radius;
224   };
225 
226   struct ProgressBarExtraParams {
227     double animated_seconds;
228     bool determinate;
229     int value_rect_x;
230     int value_rect_y;
231     int value_rect_width;
232     int value_rect_height;
233   };
234 
235   struct ScrollbarArrowExtraParams {
236     bool is_hovering;
237     float zoom;
238     bool right_to_left;
239   };
240 
241   struct ScrollbarTrackExtraParams {
242     bool is_upper;
243     int track_x;
244     int track_y;
245     int track_width;
246     int track_height;
247     int classic_state;  // Used on Windows when uxtheme is not available.
248   };
249 
250   enum ScrollbarOverlayColorTheme {
251     ScrollbarOverlayColorThemeDark,
252     ScrollbarOverlayColorThemeLight
253   };
254 
255   struct ScrollbarThumbExtraParams {
256     bool is_hovering;
257     ScrollbarOverlayColorTheme scrollbar_theme;
258   };
259 
260 #if defined(OS_APPLE)
261   enum ScrollbarOrientation {
262     // Vertical scrollbar on the right side of content.
263     kVerticalOnRight,
264     // Vertical scrollbar on the left side of content.
265     kVerticalOnLeft,
266     // Horizontal scrollbar (on the bottom of content).
267     kHorizontal,
268   };
269 
270   // A unique set of scrollbar params. Currently needed for Mac.
271   struct ScrollbarExtraParams {
272     bool is_hovering;
273     bool is_overlay;
274     ScrollbarOverlayColorTheme scrollbar_theme;
275     ScrollbarOrientation orientation;  // Used on Mac for drawing gradients.
276   };
277 #endif
278 
279   struct SliderExtraParams {
280     bool vertical;
281     bool in_drag;
282     int thumb_x;
283     int thumb_y;
284     float zoom;
285     bool right_to_left;
286   };
287 
288   struct TextFieldExtraParams {
289     bool is_text_area;
290     bool is_listbox;
291     SkColor background_color;
292     bool is_read_only;
293     bool is_focused;
294     bool fill_content_area;
295     bool draw_edges;
296     int classic_state;  // Used on Windows when uxtheme is not available.
297     bool has_border;
298     bool auto_complete_active;
299   };
300 
301   struct TrackbarExtraParams {
302     bool vertical;
303     int classic_state;  // Used on Windows when uxtheme is not available.
304   };
305 
306   union NATIVE_THEME_EXPORT ExtraParams {
307     ExtraParams();
308     ExtraParams(const ExtraParams& other);
309 
310     ButtonExtraParams button;
311     FrameTopAreaExtraParams frame_top_area;
312     InnerSpinButtonExtraParams inner_spin;
313     MenuArrowExtraParams menu_arrow;
314     MenuCheckExtraParams menu_check;
315     MenuItemExtraParams menu_item;
316     MenuSeparatorExtraParams menu_separator;
317     MenuListExtraParams menu_list;
318     MenuBackgroundExtraParams menu_background;
319     ProgressBarExtraParams progress_bar;
320     ScrollbarArrowExtraParams scrollbar_arrow;
321 #if defined(OS_APPLE)
322     ScrollbarExtraParams scrollbar_extra;
323 #endif
324     ScrollbarTrackExtraParams scrollbar_track;
325     ScrollbarThumbExtraParams scrollbar_thumb;
326     SliderExtraParams slider;
327     TextFieldExtraParams text_field;
328     TrackbarExtraParams trackbar;
329   };
330 
331   // Return the size of the part.
332   virtual gfx::Size GetPartSize(Part part,
333                                 State state,
334                                 const ExtraParams& extra) const = 0;
335 
336   virtual float GetBorderRadiusForPart(Part part,
337                                        float width,
338                                        float height,
339                                        float zoom) const;
340 
341   // Paint the part to the canvas.
342   virtual void Paint(
343       cc::PaintCanvas* canvas,
344       Part part,
345       State state,
346       const gfx::Rect& rect,
347       const ExtraParams& extra,
348       ColorScheme color_scheme = ColorScheme::kDefault) const = 0;
349 
350   // Paint part during state transition, used for overlay scrollbar state
351   // transition animation.
PaintStateTransition(cc::PaintCanvas * canvas,Part part,State startState,State endState,double progress,const gfx::Rect & rect,ScrollbarOverlayColorTheme theme)352   virtual void PaintStateTransition(cc::PaintCanvas* canvas,
353                                     Part part,
354                                     State startState,
355                                     State endState,
356                                     double progress,
357                                     const gfx::Rect& rect,
358                                     ScrollbarOverlayColorTheme theme) const {}
359 
360   // Returns whether the theme uses a nine-patch resource for the given part.
361   // If true, calling code should always paint into a canvas the size of which
362   // can be gotten from GetNinePatchCanvasSize.
363   virtual bool SupportsNinePatch(Part part) const = 0;
364 
365   // If the part paints into a nine-patch resource, the size of the canvas
366   // which should be painted into.
367   virtual gfx::Size GetNinePatchCanvasSize(Part part) const = 0;
368 
369   // If the part paints into a nine-patch resource, the rect in the canvas
370   // which defines the center tile. This is the tile that should be resized out
371   // when the part is resized.
372   virtual gfx::Rect GetNinePatchAperture(Part part) const = 0;
373 
374   // Colors for GetSystemColor().
375   enum ColorId {
376 #define OP(enum_name) enum_name
377     NATIVE_THEME_COLOR_IDS,
378 #undef OP
379 
380     kColorId_NumColors,
381   };
382 
383   enum class SystemThemeColor {
384     kNotSupported,
385     kButtonFace,
386     kButtonText,
387     kGrayText,
388     kHighlight,
389     kHighlightText,
390     kHotlight,
391     kMenuHighlight,
392     kScrollbar,
393     kWindow,
394     kWindowText,
395     kMaxValue = kWindowText,
396   };
397 
398   // Return a color from the system theme.
399   virtual SkColor GetSystemColor(
400       ColorId color_id,
401       ColorScheme color_scheme = ColorScheme::kDefault) const;
402 
403   // Returns a shared instance of the native theme that should be used for web
404   // rendering. Do not use it in a normal application context (i.e. browser).
405   // The returned object should not be deleted by the caller. This function is
406   // not thread safe and should only be called from the UI thread. Each port of
407   // NativeTheme should provide its own implementation of this function,
408   // returning the port's subclass.
409   static NativeTheme* GetInstanceForWeb();
410 
411   // Returns a shared instance of the default native theme for native UI.
412   static NativeTheme* GetInstanceForNativeUi();
413 
414   // Returns a shared instance of the native theme for incognito UI.
415   static NativeTheme* GetInstanceForDarkUI();
416 
417   // Whether OS-level dark mode is available in the current OS.
418   static bool SystemDarkModeSupported();
419 
420   // Add or remove observers to be notified when the native theme changes.
421   void AddObserver(NativeThemeObserver* observer);
422   void RemoveObserver(NativeThemeObserver* observer);
423 
424   // Notify observers of native theme changes.
425   void NotifyObservers();
426 
427   // Returns whether this NativeTheme uses higher-contrast colors, controlled by
428   // system accessibility settings and the system theme.
429   virtual bool UsesHighContrastColors() const;
430 
431   // Returns the PlatformHighContrastColorScheme used by the OS. Returns a value
432   // other than kNone only if the default system color scheme is
433   // kPlatformHighContrast.
434   PlatformHighContrastColorScheme GetPlatformHighContrastColorScheme() const;
435 
436   // Returns true when the NativeTheme uses a light-on-dark color scheme. If
437   // you're considering using this function to choose between two hard-coded
438   // colors, you probably shouldn't. Instead, use GetSystemColor().
439   virtual bool ShouldUseDarkColors() const;
440 
441   // Returns the OS-level user preferred color scheme. See the comment for
442   // CalculatePreferredColorScheme() for details on how preferred color scheme
443   // is calculated.
444   virtual PreferredColorScheme GetPreferredColorScheme() const;
445 
446   // Returns the OS-level user preferred contrast.
447   virtual PreferredContrast GetPreferredContrast() const;
448 
449   // Returns the system's caption style.
450   virtual base::Optional<CaptionStyle> GetSystemCaptionStyle() const;
451 
452   virtual ColorScheme GetDefaultSystemColorScheme() const;
453 
454   virtual const std::map<SystemThemeColor, SkColor>& GetSystemColors() const;
455 
456   base::Optional<SkColor> GetSystemThemeColor(
457       SystemThemeColor theme_color) const;
458 
459   bool HasDifferentSystemColors(
460       const std::map<SystemThemeColor, SkColor>& colors) const;
461 
set_use_dark_colors(bool should_use_dark_colors)462   void set_use_dark_colors(bool should_use_dark_colors) {
463     should_use_dark_colors_ = should_use_dark_colors;
464   }
set_high_contrast(bool is_high_contrast)465   void set_high_contrast(bool is_high_contrast) {
466     is_high_contrast_ = is_high_contrast;
467   }
set_preferred_color_scheme(PreferredColorScheme preferred_color_scheme)468   void set_preferred_color_scheme(PreferredColorScheme preferred_color_scheme) {
469     preferred_color_scheme_ = preferred_color_scheme;
470   }
set_preferred_contrast(PreferredContrast preferred_contrast)471   void set_preferred_contrast(PreferredContrast preferred_contrast) {
472     preferred_contrast_ = preferred_contrast;
473   }
474   void set_system_colors(const std::map<SystemThemeColor, SkColor>& colors);
475 
476   // Updates the state of dark mode, high contrast, and the map of system
477   // colors. Returns true if NativeTheme was updated as a result, or false if
478   // the state of NativeTheme was untouched.
479   bool UpdateSystemColorInfo(
480       bool is_dark_mode,
481       bool is_high_contrast,
482       const base::flat_map<SystemThemeColor, uint32_t>& colors);
483 
484   // On certain platforms, currently only Mac, there is a unique visual for
485   // pressed states.
486   virtual SkColor GetSystemButtonPressedColor(SkColor base_color) const;
487 
488   // Assign the focus-ring-appropriate alpha value to the provided base_color.
489   virtual SkColor FocusRingColorForBaseColor(SkColor base_color) const;
490 
491  protected:
492   explicit NativeTheme(bool should_only_use_dark_colors);
493   virtual ~NativeTheme();
494 
495   // Whether high contrast is forced via command-line flag.
496   bool IsForcedHighContrast() const;
497   // Whether dark mode is forced via command-line flag.
498   bool IsForcedDarkMode() const;
499 
500   // Calculates and returns the current user preferred color scheme. The
501   // base behavior is to set preferred color scheme to light or dark depending
502   // on the state of dark mode.
503   //
504   // Some platforms override this behavior. On Windows, for example, we also
505   // look at the high contrast setting. If high contrast is enabled, the
506   // preferred color scheme calculation will ignore the state of dark mode.
507   // Instead, preferred color scheme will be light, or dark depending on the OS
508   // high contrast theme. If high contrast is off, the preferred color scheme
509   // calculation will follow the default behavior.
510   virtual PreferredColorScheme CalculatePreferredColorScheme() const;
511 
512   // Calculates and returns the current user preferred contrast.
513   virtual PreferredContrast CalculatePreferredContrast() const;
514 
515   // A function to be called by native theme instances that need to set state
516   // or listeners with the webinstance in order to provide correct native
517   // platform behaviors.
ConfigureWebInstance()518   virtual void ConfigureWebInstance() {}
519 
520   // Allows one native theme to observe changes in another. For example, the
521   // web native theme for Windows observes the corresponding ui native theme in
522   // order to receive changes regarding the state of dark mode, high contrast,
523   // preferred color scheme and preferred contrast.
524   class NATIVE_THEME_EXPORT ColorSchemeNativeThemeObserver
525       : public NativeThemeObserver {
526    public:
527     ColorSchemeNativeThemeObserver(NativeTheme* theme_to_update);
528     ~ColorSchemeNativeThemeObserver() override;
529 
530    private:
531     // ui::NativeThemeObserver:
532     void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
533 
534     // The theme that gets updated when OnNativeThemeUpdated() is called.
535     NativeTheme* const theme_to_update_;
536 
537     DISALLOW_COPY_AND_ASSIGN(ColorSchemeNativeThemeObserver);
538   };
539 
540   mutable std::map<SystemThemeColor, SkColor> system_colors_;
541 
542  private:
543   // Observers to notify when the native theme changes.
544   base::ObserverList<NativeThemeObserver>::Unchecked native_theme_observers_;
545 
546   bool should_use_dark_colors_ = false;
547   bool is_high_contrast_ = false;
548   PreferredColorScheme preferred_color_scheme_ = PreferredColorScheme::kLight;
549   PreferredContrast preferred_contrast_ = PreferredContrast::kNoPreference;
550 
551   DISALLOW_COPY_AND_ASSIGN(NativeTheme);
552 };
553 
554 }  // namespace ui
555 
556 #endif  // UI_NATIVE_THEME_NATIVE_THEME_H_
557