OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/libgtkui/native_theme_gtk3.h" | 5 #include "chrome/browser/ui/libgtkui/native_theme_gtk3.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 | 8 |
9 #include "base/strings/string_split.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "chrome/browser/ui/libgtkui/chrome_gtk_frame.h" | 9 #include "chrome/browser/ui/libgtkui/chrome_gtk_frame.h" |
12 #include "chrome/browser/ui/libgtkui/chrome_gtk_menu_subclasses.h" | 10 #include "chrome/browser/ui/libgtkui/chrome_gtk_menu_subclasses.h" |
13 #include "chrome/browser/ui/libgtkui/gtk_util.h" | 11 #include "chrome/browser/ui/libgtkui/gtk_util.h" |
14 #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" | 12 #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" |
| 13 #include "ui/gfx/color_palette.h" |
15 #include "ui/gfx/color_utils.h" | 14 #include "ui/gfx/color_utils.h" |
16 #include "ui/gfx/geometry/rect.h" | 15 #include "ui/gfx/geometry/rect.h" |
17 #include "ui/native_theme/native_theme_dark_aura.h" | 16 #include "ui/native_theme/native_theme_dark_aura.h" |
18 | 17 |
19 namespace libgtkui { | 18 namespace libgtkui { |
20 | 19 |
21 namespace { | 20 namespace { |
22 | 21 |
23 enum WidgetState { | |
24 NORMAL = 0, | |
25 ACTIVE = 1, | |
26 PRELIGHT = 2, | |
27 SELECTED = 3, | |
28 INSENSITIVE = 4, | |
29 }; | |
30 | |
31 // Same order as enum WidgetState above | |
32 const GtkStateFlags stateMap[] = { | |
33 GTK_STATE_FLAG_NORMAL, GTK_STATE_FLAG_ACTIVE, | |
34 GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_SELECTED, | |
35 GTK_STATE_FLAG_INSENSITIVE, | |
36 }; | |
37 | |
38 // The caller must g_object_unref the returned context. | |
39 GtkStyleContext* GetStyleContextFromCss(const char* css_selector) { | |
40 GtkWidgetPath* path = gtk_widget_path_new(); | |
41 for (const auto& widget_type : | |
42 base::SplitString(css_selector, base::kWhitespaceASCII, | |
43 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { | |
44 gtk_widget_path_append_type(path, G_TYPE_NONE); | |
45 for (const auto& widget_class : | |
46 base::SplitString(widget_type, ".", base::TRIM_WHITESPACE, | |
47 base::SPLIT_WANT_NONEMPTY)) { | |
48 gtk_widget_path_iter_add_class(path, -1, widget_class.c_str()); | |
49 } | |
50 } | |
51 | |
52 GtkStyleContext* context = gtk_style_context_new(); | |
53 gtk_style_context_set_path(context, path); | |
54 gtk_widget_path_unref(path); | |
55 return context; | |
56 } | |
57 | |
58 SkColor GdkRgbaToSkColor(const GdkRGBA& color) { | |
59 return SkColorSetARGB(color.alpha * 255, color.red * 255, color.green * 255, | |
60 color.blue * 255); | |
61 } | |
62 | |
63 SkColor ColorFromContext(GtkStyleContext* context, | |
64 GtkStateFlags state, | |
65 const char* color_name) { | |
66 GdkRGBA* color = nullptr; | |
67 gtk_style_context_get(context, state, color_name, &color, nullptr); | |
68 DCHECK(color); | |
69 SkColor sk_color = GdkRgbaToSkColor(*color); | |
70 gdk_rgba_free(color); | |
71 return sk_color; | |
72 } | |
73 | |
74 SkColor GetFGColor(GtkWidget* widget, WidgetState state) { | |
75 return ColorFromContext(gtk_widget_get_style_context(widget), stateMap[state], | |
76 "color"); | |
77 } | |
78 | |
79 SkColor GetBGColor(GtkWidget* widget, WidgetState state) { | |
80 return ColorFromContext(gtk_widget_get_style_context(widget), stateMap[state], | |
81 "background-color"); | |
82 } | |
83 | |
84 SkColor GetFGColor(const char* css_selector, GtkStateFlags state) { | |
85 GtkStyleContext* context = GetStyleContextFromCss(css_selector); | |
86 SkColor color = ColorFromContext(context, state, "color"); | |
87 g_object_unref(context); | |
88 return color; | |
89 } | |
90 | |
91 SkColor GetBGColor(const char* css_selector, GtkStateFlags state) { | |
92 GtkStyleContext* context = GetStyleContextFromCss(css_selector); | |
93 SkColor color = ColorFromContext(context, state, "background-color"); | |
94 g_object_unref(context); | |
95 return color; | |
96 } | |
97 | |
98 SkColor GetBorderColor(const char* css_selector, GtkStateFlags state) { | |
99 GtkStyleContext* context = GetStyleContextFromCss(css_selector); | |
100 GtkBorder border; | |
101 gtk_style_context_get_border(context, state, &border); | |
102 bool has_border = border.left || border.right || border.top || border.bottom; | |
103 SkColor color = ColorFromContext( | |
104 context, state, has_border ? "border-color" : "background-color"); | |
105 g_object_unref(context); | |
106 return color; | |
107 } | |
108 | |
109 void PaintWidget(SkCanvas* canvas, | 22 void PaintWidget(SkCanvas* canvas, |
110 const gfx::Rect& rect, | 23 const gfx::Rect& rect, |
111 const char* css_selector, | 24 const char* css_selector, |
112 GtkStateFlags state) { | 25 GtkStateFlags state) { |
113 SkBitmap bitmap; | 26 SkBitmap bitmap; |
114 bitmap.allocN32Pixels(rect.width(), rect.height()); | 27 bitmap.allocN32Pixels(rect.width(), rect.height()); |
115 bitmap.eraseColor(0); | 28 bitmap.eraseColor(0); |
116 | 29 |
117 cairo_surface_t* surface = cairo_image_surface_create_for_data( | 30 cairo_surface_t* surface = cairo_image_surface_create_for_data( |
118 static_cast<unsigned char*>(bitmap.getAddr(0, 0)), CAIRO_FORMAT_ARGB32, | 31 static_cast<unsigned char*>(bitmap.getAddr(0, 0)), CAIRO_FORMAT_ARGB32, |
119 rect.width(), rect.height(), | 32 rect.width(), rect.height(), |
120 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, rect.width())); | 33 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, rect.width())); |
121 cairo_t* cr = cairo_create(surface); | 34 cairo_t* cr = cairo_create(surface); |
122 | 35 |
123 GtkStyleContext* context = GetStyleContextFromCss(css_selector); | 36 auto context = GetStyleContextFromCss(css_selector); |
124 gtk_style_context_set_state(context, state); | 37 gtk_style_context_set_state(context, state); |
125 | 38 |
126 gtk_render_background(context, cr, 0, 0, rect.width(), rect.height()); | 39 gtk_render_background(context, cr, 0, 0, rect.width(), rect.height()); |
127 gtk_render_frame(context, cr, 0, 0, rect.width(), rect.height()); | 40 gtk_render_frame(context, cr, 0, 0, rect.width(), rect.height()); |
128 cairo_destroy(cr); | 41 cairo_destroy(cr); |
129 cairo_surface_destroy(surface); | 42 cairo_surface_destroy(surface); |
130 canvas->drawBitmap(bitmap, rect.x(), rect.y()); | 43 canvas->drawBitmap(bitmap, rect.x(), rect.y()); |
131 | |
132 g_object_unref(context); | |
133 } | 44 } |
134 | 45 |
135 GtkStateFlags StateToStateFlags(NativeThemeGtk3::State state) { | 46 GtkStateFlags StateToStateFlags(NativeThemeGtk3::State state) { |
136 switch (state) { | 47 switch (state) { |
137 case NativeThemeGtk3::kDisabled: | 48 case NativeThemeGtk3::kDisabled: |
138 return GTK_STATE_FLAG_INSENSITIVE; | 49 return GTK_STATE_FLAG_INSENSITIVE; |
139 case NativeThemeGtk3::kHovered: | 50 case NativeThemeGtk3::kHovered: |
140 return GTK_STATE_FLAG_PRELIGHT; | 51 return GTK_STATE_FLAG_PRELIGHT; |
141 case NativeThemeGtk3::kNormal: | 52 case NativeThemeGtk3::kNormal: |
142 return GTK_STATE_FLAG_NORMAL; | 53 return GTK_STATE_FLAG_NORMAL; |
143 case NativeThemeGtk3::kPressed: | 54 case NativeThemeGtk3::kPressed: |
144 return static_cast<GtkStateFlags>(GTK_STATE_FLAG_PRELIGHT | | 55 return static_cast<GtkStateFlags>(GTK_STATE_FLAG_PRELIGHT | |
145 GTK_STATE_FLAG_ACTIVE); | 56 GTK_STATE_FLAG_ACTIVE); |
146 default: | 57 default: |
147 NOTREACHED(); | 58 NOTREACHED(); |
148 return GTK_STATE_FLAG_NORMAL; | 59 return GTK_STATE_FLAG_NORMAL; |
149 } | 60 } |
150 } | 61 } |
151 | 62 |
| 63 SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) { |
| 64 const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43); |
| 65 const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29); |
| 66 |
| 67 switch (color_id) { |
| 68 // Windows |
| 69 case ui::NativeTheme::kColorId_WindowBackground: |
| 70 // Dialogs |
| 71 case ui::NativeTheme::kColorId_DialogBackground: |
| 72 case ui::NativeTheme::kColorId_BubbleBackground: |
| 73 return GetBGColor(""); |
| 74 |
| 75 // FocusableBorder |
| 76 case ui::NativeTheme::kColorId_FocusedBorderColor: |
| 77 return GetBorderColor("GtkEntry.entry:focus"); |
| 78 case ui::NativeTheme::kColorId_UnfocusedBorderColor: |
| 79 return GetBorderColor("GtkEntry.entry"); |
| 80 |
| 81 // Menu |
| 82 case ui::NativeTheme::kColorId_MenuBackgroundColor: |
| 83 return GetBGColor("GtkMenu.menu"); |
| 84 case ui::NativeTheme::kColorId_MenuBorderColor: |
| 85 return GetBorderColor("GtkMenu.menu"); |
| 86 case ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor: |
| 87 return GetBGColor("GtkMenu.menu GtkMenuItem.menuitem:focus"); |
| 88 case ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor: |
| 89 return GetFGColor("GtkMenu.menu GtkMenuItem.menuitem GtkLabel.label"); |
| 90 case ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor: |
| 91 return GetFGColor( |
| 92 "GtkMenu.menu GtkMenuItem.menuitem:selected GtkLabel.label"); |
| 93 case ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor: |
| 94 return GetFGColor( |
| 95 "GtkMenu.menu GtkMenuItem.menuitem:disabled GtkLabel.label"); |
| 96 case ui::NativeTheme::kColorId_MenuItemSubtitleColor: |
| 97 return GetFGColor( |
| 98 "GtkMenu.menu GtkMenuItem.menuitem GtkLabel.label.accelerator"); |
| 99 case ui::NativeTheme::kColorId_MenuSeparatorColor: |
| 100 // MenuButton borders are used the same way as menu separators in Chrome. |
| 101 case ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor: |
| 102 case ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor: |
| 103 case ui::NativeTheme::kColorId_HoverMenuButtonBorderColor: |
| 104 return GetFGColor("GtkMenu.menu GtkMenuItem.menuitem.separator:disabled"); |
| 105 |
| 106 // Label |
| 107 case ui::NativeTheme::kColorId_LabelEnabledColor: |
| 108 return GetFGColor("GtkLabel.label"); |
| 109 case ui::NativeTheme::kColorId_LabelDisabledColor: |
| 110 return GetFGColor("GtkLabel.label:disabled"); |
| 111 case ui::NativeTheme::kColorId_LabelTextSelectionColor: |
| 112 return GetFGColor("GtkLabel.label .selection:selected"); |
| 113 case ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused: |
| 114 return GetBGColor("GtkLabel.label .selection:selected"); |
| 115 |
| 116 // Link |
| 117 case ui::NativeTheme::kColorId_LinkDisabled: |
| 118 return SkColorSetA( |
| 119 SkColorFromColorId(ui::NativeTheme::kColorId_LinkEnabled), 0xBB); |
| 120 case ui::NativeTheme::kColorId_LinkPressed: |
| 121 case ui::NativeTheme::kColorId_LinkEnabled: { |
| 122 // TODO(thomasanderson): Gtk changed the way links are colored in 3.12. |
| 123 // Add code for later versions. |
| 124 auto link_context = GetStyleContextFromCss("GtkLabel.label.view"); |
| 125 GdkColor* color; |
| 126 gtk_style_context_get_style(link_context, "link-color", &color, nullptr); |
| 127 if (color) { |
| 128 SkColor ret_color = SkColorSetRGB(color->red / 255, color->green / 255, |
| 129 color->blue / 255); |
| 130 gdk_color_free(color); |
| 131 return ret_color; |
| 132 } else { |
| 133 // Default color comes from gtklinkbutton.c. |
| 134 return SkColorSetRGB(0x00, 0x00, 0xEE); |
| 135 } |
| 136 } |
| 137 |
| 138 // Button |
| 139 case ui::NativeTheme::kColorId_ButtonEnabledColor: |
| 140 return GetFGColor("GtkButton.button.text-button GtkLabel.label"); |
| 141 case ui::NativeTheme::kColorId_ButtonDisabledColor: |
| 142 return GetFGColor("GtkButton.button.text-button:disabled GtkLabel.label"); |
| 143 case ui::NativeTheme::kColorId_ButtonHoverColor: |
| 144 return GetFGColor("GtkButton.button.text-button:hover GtkLabel.label"); |
| 145 case ui::NativeTheme::kColorId_ButtonPressedShade: |
| 146 return SK_ColorTRANSPARENT; |
| 147 |
| 148 case ui::NativeTheme::kColorId_BlueButtonEnabledColor: |
| 149 return GetFGColor( |
| 150 "GtkButton.button.text-button.suggested-action GtkLabel.label"); |
| 151 case ui::NativeTheme::kColorId_BlueButtonDisabledColor: |
| 152 return GetFGColor( |
| 153 "GtkButton.button.text-button.suggested-action:disabled " |
| 154 "GtkLabel.label"); |
| 155 case ui::NativeTheme::kColorId_BlueButtonHoverColor: |
| 156 return GetFGColor( |
| 157 "GtkButton.button.text-button.suggested-action:hover GtkLabel.label"); |
| 158 case ui::NativeTheme::kColorId_BlueButtonPressedColor: |
| 159 return GetFGColor( |
| 160 "GtkButton.button.text-button.suggested-action:hover:active " |
| 161 "GtkLabel.label"); |
| 162 case ui::NativeTheme::kColorId_BlueButtonShadowColor: |
| 163 return SK_ColorTRANSPARENT; |
| 164 |
| 165 case ui::NativeTheme::kColorId_ProminentButtonColor: |
| 166 return GetBGColor("GtkButton.button.text-button.destructive-action"); |
| 167 case ui::NativeTheme::kColorId_TextOnProminentButtonColor: |
| 168 return GetFGColor( |
| 169 "GtkButton.button.text-button.destructive-action GtkLabel.label"); |
| 170 |
| 171 // Textfield |
| 172 case ui::NativeTheme::kColorId_TextfieldDefaultColor: |
| 173 return GetFGColor("GtkEntry.entry"); |
| 174 case ui::NativeTheme::kColorId_TextfieldDefaultBackground: |
| 175 return GetBGColor("GtkEntry.entry"); |
| 176 case ui::NativeTheme::kColorId_TextfieldReadOnlyColor: |
| 177 return GetFGColor("GtkEntry.entry:disabled"); |
| 178 case ui::NativeTheme::kColorId_TextfieldReadOnlyBackground: |
| 179 return GetBGColor("GtkEntry.entry:disabled"); |
| 180 case ui::NativeTheme::kColorId_TextfieldSelectionColor: |
| 181 return GetFGColor("GtkEntry.entry .selection:selected"); |
| 182 case ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused: |
| 183 return GetBGColor("GtkEntry.entry .selection:selected"); |
| 184 |
| 185 // Tooltips |
| 186 case ui::NativeTheme::kColorId_TooltipBackground: |
| 187 return GetBGColor("GtkTooltip.tooltip"); |
| 188 case ui::NativeTheme::kColorId_TooltipText: |
| 189 return color_utils::GetReadableColor(GetFGColor("GtkTooltip.tooltip"), |
| 190 GetBGColor("GtkTooltip.tooltip")); |
| 191 |
| 192 // Trees and Tables (implemented on GTK using the same class) |
| 193 case ui::NativeTheme::kColorId_TableBackground: |
| 194 case ui::NativeTheme::kColorId_TreeBackground: |
| 195 return GetBGColor("GtkTreeView.view"); |
| 196 case ui::NativeTheme::kColorId_TableText: |
| 197 case ui::NativeTheme::kColorId_TreeText: |
| 198 case ui::NativeTheme::kColorId_TreeArrow: |
| 199 case ui::NativeTheme::kColorId_TableGroupingIndicatorColor: |
| 200 return GetFGColor("GtkTreeView.view .cell"); |
| 201 case ui::NativeTheme::kColorId_TableSelectedText: |
| 202 case ui::NativeTheme::kColorId_TableSelectedTextUnfocused: |
| 203 case ui::NativeTheme::kColorId_TreeSelectedText: |
| 204 case ui::NativeTheme::kColorId_TreeSelectedTextUnfocused: |
| 205 return GetFGColor("GtkTreeView.view .cell:selected"); |
| 206 case ui::NativeTheme::kColorId_TableSelectionBackgroundFocused: |
| 207 case ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused: |
| 208 case ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused: |
| 209 case ui::NativeTheme::kColorId_TreeSelectionBackgroundUnfocused: |
| 210 return GetBGColor("GtkTreeView.view .cell:selected"); |
| 211 |
| 212 // Results Table |
| 213 // TODO(thomasanderson): The GtkEntry selectors was how the gtk2 theme got |
| 214 // these colors. Update this code to use a different widget. |
| 215 case ui::NativeTheme::kColorId_ResultsTableNormalBackground: |
| 216 return GetBGColor("GtkEntry.entry"); |
| 217 case ui::NativeTheme::kColorId_ResultsTableHoveredBackground: |
| 218 return color_utils::AlphaBlend( |
| 219 GetBGColor("GtkEntry.entry"), |
| 220 GetBGColor("GtkEntry.entry .selection:selected"), 0x80); |
| 221 case ui::NativeTheme::kColorId_ResultsTableSelectedBackground: |
| 222 return GetBGColor("GtkEntry.entry .selection:selected"); |
| 223 case ui::NativeTheme::kColorId_ResultsTableNormalText: |
| 224 case ui::NativeTheme::kColorId_ResultsTableHoveredText: |
| 225 return GetFGColor("GtkEntry.entry"); |
| 226 case ui::NativeTheme::kColorId_ResultsTableSelectedText: |
| 227 return GetFGColor("GtkEntry.entry .selection:selected"); |
| 228 case ui::NativeTheme::kColorId_ResultsTableNormalDimmedText: |
| 229 case ui::NativeTheme::kColorId_ResultsTableHoveredDimmedText: |
| 230 return color_utils::AlphaBlend(GetFGColor("GtkEntry.entry"), |
| 231 GetBGColor("GtkEntry.entry"), 0x80); |
| 232 case ui::NativeTheme::kColorId_ResultsTableSelectedDimmedText: |
| 233 return color_utils::AlphaBlend( |
| 234 GetFGColor("GtkEntry.entry .selection:selected"), |
| 235 GetBGColor("GtkEntry.entry"), 0x80); |
| 236 case ui::NativeTheme::kColorId_ResultsTableNormalUrl: |
| 237 case ui::NativeTheme::kColorId_ResultsTableHoveredUrl: |
| 238 return NormalURLColor(GetFGColor("GtkEntry.entry")); |
| 239 case ui::NativeTheme::kColorId_ResultsTableSelectedUrl: |
| 240 return SelectedURLColor(GetFGColor("GtkEntry.entry .selection:selected"), |
| 241 GetBGColor("GtkEntry.entry .selection:selected")); |
| 242 |
| 243 case ui::NativeTheme::kColorId_ResultsTablePositiveText: |
| 244 return color_utils::GetReadableColor(kPositiveTextColor, |
| 245 GetBGColor("GtkEntry.entry")); |
| 246 case ui::NativeTheme::kColorId_ResultsTablePositiveHoveredText: |
| 247 return color_utils::GetReadableColor(kPositiveTextColor, |
| 248 GetBGColor("GtkEntry.entry:hover")); |
| 249 case ui::NativeTheme::kColorId_ResultsTablePositiveSelectedText: |
| 250 return color_utils::GetReadableColor( |
| 251 kPositiveTextColor, GetBGColor("GtkEntry.entry:selected")); |
| 252 case ui::NativeTheme::kColorId_ResultsTableNegativeText: |
| 253 return color_utils::GetReadableColor(kNegativeTextColor, |
| 254 GetBGColor("GtkEntry.entry")); |
| 255 case ui::NativeTheme::kColorId_ResultsTableNegativeHoveredText: |
| 256 return color_utils::GetReadableColor(kNegativeTextColor, |
| 257 GetBGColor("GtkEntry.entry:hover")); |
| 258 case ui::NativeTheme::kColorId_ResultsTableNegativeSelectedText: |
| 259 return color_utils::GetReadableColor( |
| 260 kNegativeTextColor, GetBGColor("GtkEntry.entry:selected")); |
| 261 |
| 262 // Throbber |
| 263 // TODO(thomasanderson): Render GtkSpinner directly. |
| 264 case ui::NativeTheme::kColorId_ThrobberSpinningColor: |
| 265 case ui::NativeTheme::kColorId_ThrobberWaitingColor: |
| 266 return GetFGColor("GtkMenu.menu GtkSpinner.spinner"); |
| 267 case ui::NativeTheme::kColorId_ThrobberLightColor: |
| 268 return GetFGColor("GtkMenu.menu GtkSpinner.spinner:disabled"); |
| 269 |
| 270 // Alert icons |
| 271 case ui::NativeTheme::kColorId_AlertSeverityLow: |
| 272 return GetBGColor("GtkInfoBar.infobar.info"); |
| 273 case ui::NativeTheme::kColorId_AlertSeverityMedium: |
| 274 return GetBGColor("GtkInfoBar.infobar.warning"); |
| 275 case ui::NativeTheme::kColorId_AlertSeverityHigh: |
| 276 return GetBGColor("GtkInfoBar.infobar.error"); |
| 277 |
| 278 case ui::NativeTheme::kColorId_NumColors: |
| 279 NOTREACHED(); |
| 280 break; |
| 281 } |
| 282 return kInvalidColorIdColor; |
| 283 } |
| 284 |
| 285 void OnThemeChanged(GObject* obj, GParamSpec* param, NativeThemeGtk3* theme) { |
| 286 theme->ResetColorCache(); |
| 287 } |
| 288 |
152 } // namespace | 289 } // namespace |
153 | 290 |
154 // static | 291 // static |
155 NativeThemeGtk3* NativeThemeGtk3::instance() { | 292 NativeThemeGtk3* NativeThemeGtk3::instance() { |
156 CR_DEFINE_STATIC_LOCAL(NativeThemeGtk3, s_native_theme, ()); | 293 CR_DEFINE_STATIC_LOCAL(NativeThemeGtk3, s_native_theme, ()); |
157 return &s_native_theme; | 294 return &s_native_theme; |
158 } | 295 } |
159 | 296 |
160 // Constructors automatically called | 297 // Constructors automatically called |
161 NativeThemeGtk3::NativeThemeGtk3() {} | 298 NativeThemeGtk3::NativeThemeGtk3() { |
| 299 // These types are needed by g_type_from_name(), but may not be registered at |
| 300 // this point. We need the g_type_class magic to make sure the compiler |
| 301 // doesn't optimize away this code. |
| 302 g_type_class_unref(g_type_class_ref(gtk_button_get_type())); |
| 303 g_type_class_unref(g_type_class_ref(gtk_label_get_type())); |
| 304 g_type_class_unref(g_type_class_ref(gtk_window_get_type())); |
| 305 g_type_class_unref(g_type_class_ref(gtk_link_button_get_type())); |
| 306 g_type_class_unref(g_type_class_ref(gtk_spinner_get_type())); |
| 307 g_type_class_unref(g_type_class_ref(gtk_menu_get_type())); |
| 308 g_type_class_unref(g_type_class_ref(gtk_menu_item_get_type())); |
| 309 g_type_class_unref(g_type_class_ref(gtk_entry_get_type())); |
| 310 g_type_class_unref(g_type_class_ref(gtk_info_bar_get_type())); |
| 311 g_type_class_unref(g_type_class_ref(gtk_tooltip_get_type())); |
| 312 g_type_class_unref(g_type_class_ref(gtk_scrollbar_get_type())); |
| 313 g_type_class_unref(g_type_class_ref(gtk_toolbar_get_type())); |
| 314 g_type_class_unref(g_type_class_ref(gtk_text_view_get_type())); |
| 315 |
| 316 g_signal_connect_after(gtk_settings_get_default(), "notify::gtk-theme-name", |
| 317 G_CALLBACK(OnThemeChanged), this); |
| 318 } |
| 319 |
162 // This doesn't actually get called | 320 // This doesn't actually get called |
163 NativeThemeGtk3::~NativeThemeGtk3() {} | 321 NativeThemeGtk3::~NativeThemeGtk3() {} |
164 | 322 |
165 SkColor NativeThemeGtk3::LookupGtkThemeColor(ColorId color_id) const { | 323 void NativeThemeGtk3::ResetColorCache() { |
166 const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43); | 324 for (auto& color : color_cache_) |
167 const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29); | 325 color = base::nullopt; |
168 | |
169 switch (color_id) { | |
170 // Windows | |
171 case kColorId_WindowBackground: | |
172 return GetBGColor(GetWindow(), SELECTED); | |
173 | |
174 // Dialogs | |
175 case kColorId_DialogBackground: | |
176 case kColorId_BubbleBackground: | |
177 return GetBGColor(GetWindow(), NORMAL); | |
178 | |
179 // FocusableBorder | |
180 case kColorId_FocusedBorderColor: | |
181 return GetBGColor(GetEntry(), SELECTED); | |
182 case kColorId_UnfocusedBorderColor: | |
183 return GetFGColor(GetEntry(), NORMAL); | |
184 | |
185 // Menu | |
186 case kColorId_MenuBackgroundColor: | |
187 return GetBGColor("menu", GTK_STATE_FLAG_NORMAL); | |
188 case kColorId_MenuBorderColor: | |
189 return GetBorderColor("menu", GTK_STATE_FLAG_NORMAL); | |
190 case kColorId_FocusedMenuItemBackgroundColor: | |
191 return GetBGColor("menu menuitem", GTK_STATE_FLAG_FOCUSED); | |
192 case kColorId_EnabledMenuItemForegroundColor: | |
193 return GetFGColor("menu menuitem label", GTK_STATE_FLAG_NORMAL); | |
194 case kColorId_SelectedMenuItemForegroundColor: | |
195 return GetFGColor("menu menuitem label", GTK_STATE_FLAG_SELECTED); | |
196 case kColorId_DisabledMenuItemForegroundColor: | |
197 return GetFGColor("menu menuitem label", GTK_STATE_FLAG_INSENSITIVE); | |
198 case kColorId_MenuItemSubtitleColor: | |
199 return GetFGColor("menu menuitem accelerator", GTK_STATE_FLAG_NORMAL); | |
200 case kColorId_MenuSeparatorColor: | |
201 // MenuButton borders are used the same way as menu separtors in Chrome. | |
202 case kColorId_EnabledMenuButtonBorderColor: | |
203 case kColorId_FocusedMenuButtonBorderColor: | |
204 case kColorId_HoverMenuButtonBorderColor: | |
205 return GetFGColor("menu menuitem.separator", GTK_STATE_FLAG_INSENSITIVE); | |
206 | |
207 // Label | |
208 case kColorId_LabelEnabledColor: | |
209 return GetFGColor(GetEntry(), NORMAL); | |
210 case kColorId_LabelDisabledColor: | |
211 return GetFGColor(GetLabel(), INSENSITIVE); | |
212 case kColorId_LabelTextSelectionColor: | |
213 return GetFGColor(GetLabel(), SELECTED); | |
214 case kColorId_LabelTextSelectionBackgroundFocused: | |
215 return GetBGColor(GetLabel(), SELECTED); | |
216 | |
217 // Link | |
218 case kColorId_LinkDisabled: | |
219 return SkColorSetA(GetSystemColor(kColorId_LinkEnabled), 0xBB); | |
220 case kColorId_LinkEnabled: { | |
221 SkColor link_color = SK_ColorTRANSPARENT; | |
222 GdkColor* style_color = nullptr; | |
223 gtk_widget_style_get(GetWindow(), "link-color", &style_color, nullptr); | |
224 if (style_color) { | |
225 link_color = GdkColorToSkColor(*style_color); | |
226 gdk_color_free(style_color); | |
227 } | |
228 if (link_color != SK_ColorTRANSPARENT) | |
229 return link_color; | |
230 // Default color comes from gtklinkbutton.c. | |
231 return SkColorSetRGB(0x00, 0x00, 0xEE); | |
232 } | |
233 case kColorId_LinkPressed: | |
234 return SK_ColorRED; | |
235 | |
236 // Button | |
237 case kColorId_ButtonEnabledColor: | |
238 return GetFGColor(GetButton(), NORMAL); | |
239 case kColorId_BlueButtonEnabledColor: | |
240 return GetFGColor(GetBlueButton(), NORMAL); | |
241 case kColorId_ButtonDisabledColor: | |
242 return GetFGColor(GetButton(), INSENSITIVE); | |
243 case kColorId_BlueButtonDisabledColor: | |
244 return GetFGColor(GetBlueButton(), INSENSITIVE); | |
245 case kColorId_ButtonHoverColor: | |
246 return GetFGColor(GetButton(), PRELIGHT); | |
247 case kColorId_BlueButtonHoverColor: | |
248 return GetFGColor(GetBlueButton(), PRELIGHT); | |
249 case kColorId_BlueButtonPressedColor: | |
250 return GetFGColor(GetBlueButton(), ACTIVE); | |
251 case kColorId_BlueButtonShadowColor: | |
252 return SK_ColorTRANSPARENT; | |
253 case kColorId_ProminentButtonColor: | |
254 return GetSystemColor(kColorId_LinkEnabled); | |
255 case kColorId_TextOnProminentButtonColor: | |
256 return GetFGColor(GetLabel(), SELECTED); | |
257 case kColorId_ButtonPressedShade: | |
258 return SK_ColorTRANSPARENT; | |
259 | |
260 // Textfield | |
261 case kColorId_TextfieldDefaultColor: | |
262 return GetFGColor(GetEntry(), NORMAL); | |
263 case kColorId_TextfieldDefaultBackground: | |
264 return GetBGColor(GetEntry(), NORMAL); | |
265 | |
266 case kColorId_TextfieldReadOnlyColor: | |
267 return GetFGColor(GetEntry(), SELECTED); | |
268 case kColorId_TextfieldReadOnlyBackground: | |
269 return GetBGColor(GetEntry(), SELECTED); | |
270 case kColorId_TextfieldSelectionColor: | |
271 return GetFGColor(GetLabel(), SELECTED); | |
272 case kColorId_TextfieldSelectionBackgroundFocused: | |
273 return GetBGColor(GetLabel(), SELECTED); | |
274 | |
275 // Tooltips | |
276 case kColorId_TooltipBackground: | |
277 return GetBGColor(GetTooltip(), NORMAL); | |
278 case kColorId_TooltipText: | |
279 return GetFGColor(GetTooltip(), NORMAL); | |
280 | |
281 // Trees and Tables (implemented on GTK using the same class) | |
282 case kColorId_TableBackground: | |
283 case kColorId_TreeBackground: | |
284 return GetBGColor(GetTree(), NORMAL); | |
285 case kColorId_TableText: | |
286 case kColorId_TreeText: | |
287 return GetFGColor(GetTree(), NORMAL); | |
288 case kColorId_TableSelectedText: | |
289 case kColorId_TableSelectedTextUnfocused: | |
290 case kColorId_TreeSelectedText: | |
291 case kColorId_TreeSelectedTextUnfocused: | |
292 return GetFGColor(GetTree(), SELECTED); | |
293 case kColorId_TableSelectionBackgroundFocused: | |
294 case kColorId_TableSelectionBackgroundUnfocused: | |
295 case kColorId_TreeSelectionBackgroundFocused: | |
296 case kColorId_TreeSelectionBackgroundUnfocused: | |
297 return GetBGColor(GetTree(), SELECTED); | |
298 case kColorId_TreeArrow: | |
299 return GetFGColor(GetTree(), NORMAL); | |
300 case kColorId_TableGroupingIndicatorColor: | |
301 return GetFGColor(GetTree(), NORMAL); | |
302 | |
303 // Results Table | |
304 case kColorId_ResultsTableNormalBackground: | |
305 return GetSystemColor(kColorId_TextfieldDefaultBackground); | |
306 case kColorId_ResultsTableHoveredBackground: | |
307 return color_utils::AlphaBlend( | |
308 GetSystemColor(kColorId_TextfieldDefaultBackground), | |
309 GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused), 0x80); | |
310 case kColorId_ResultsTableSelectedBackground: | |
311 return GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused); | |
312 case kColorId_ResultsTableNormalText: | |
313 case kColorId_ResultsTableHoveredText: | |
314 return GetSystemColor(kColorId_TextfieldDefaultColor); | |
315 case kColorId_ResultsTableSelectedText: | |
316 return GetSystemColor(kColorId_TextfieldSelectionColor); | |
317 case kColorId_ResultsTableNormalDimmedText: | |
318 case kColorId_ResultsTableHoveredDimmedText: | |
319 return color_utils::AlphaBlend( | |
320 GetSystemColor(kColorId_TextfieldDefaultColor), | |
321 GetSystemColor(kColorId_TextfieldDefaultBackground), 0x80); | |
322 case kColorId_ResultsTableSelectedDimmedText: | |
323 return color_utils::AlphaBlend( | |
324 GetSystemColor(kColorId_TextfieldSelectionColor), | |
325 GetSystemColor(kColorId_TextfieldDefaultBackground), 0x80); | |
326 case kColorId_ResultsTableNormalUrl: | |
327 case kColorId_ResultsTableHoveredUrl: | |
328 return NormalURLColor(GetSystemColor(kColorId_TextfieldDefaultColor)); | |
329 | |
330 case kColorId_ResultsTableSelectedUrl: | |
331 return SelectedURLColor( | |
332 GetSystemColor(kColorId_TextfieldSelectionColor), | |
333 GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused)); | |
334 | |
335 case kColorId_ResultsTablePositiveText: | |
336 return color_utils::GetReadableColor(kPositiveTextColor, | |
337 GetBGColor(GetEntry(), NORMAL)); | |
338 case kColorId_ResultsTablePositiveHoveredText: | |
339 return color_utils::GetReadableColor(kPositiveTextColor, | |
340 GetBGColor(GetEntry(), PRELIGHT)); | |
341 case kColorId_ResultsTablePositiveSelectedText: | |
342 return color_utils::GetReadableColor(kPositiveTextColor, | |
343 GetBGColor(GetEntry(), SELECTED)); | |
344 case kColorId_ResultsTableNegativeText: | |
345 return color_utils::GetReadableColor(kNegativeTextColor, | |
346 GetBGColor(GetEntry(), NORMAL)); | |
347 case kColorId_ResultsTableNegativeHoveredText: | |
348 return color_utils::GetReadableColor(kNegativeTextColor, | |
349 GetBGColor(GetEntry(), PRELIGHT)); | |
350 case kColorId_ResultsTableNegativeSelectedText: | |
351 return color_utils::GetReadableColor(kNegativeTextColor, | |
352 GetBGColor(GetEntry(), SELECTED)); | |
353 | |
354 // Throbber | |
355 case kColorId_ThrobberSpinningColor: | |
356 case kColorId_ThrobberLightColor: | |
357 return GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused); | |
358 | |
359 case kColorId_ThrobberWaitingColor: | |
360 return color_utils::AlphaBlend( | |
361 GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused), | |
362 GetBGColor(GetWindow(), NORMAL), 0x80); | |
363 | |
364 // Alert icons | |
365 // Just fall back to the same colors as Aura. | |
366 case kColorId_AlertSeverityLow: | |
367 case kColorId_AlertSeverityMedium: | |
368 case kColorId_AlertSeverityHigh: | |
369 return SK_ColorTRANSPARENT; | |
370 | |
371 case kColorId_NumColors: | |
372 NOTREACHED(); | |
373 break; | |
374 } | |
375 | |
376 return kInvalidColorIdColor; | |
377 } | 326 } |
378 | 327 |
379 SkColor NativeThemeGtk3::GetSystemColor(ColorId color_id) const { | 328 SkColor NativeThemeGtk3::GetSystemColor(ColorId color_id) const { |
380 SkColor color = LookupGtkThemeColor(color_id); | 329 if (color_cache_[color_id]) |
381 if (SkColorGetA(color)) | 330 return color_cache_[color_id].value(); |
382 return color; | 331 |
383 gboolean prefer_dark_theme = FALSE; | 332 SkColor color = SkColorFromColorId(color_id); |
384 g_object_get(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", | 333 color_cache_[color_id] = color; |
385 &prefer_dark_theme, nullptr); | 334 return color; |
386 ui::NativeTheme* fallback_theme = | |
387 prefer_dark_theme ? ui::NativeThemeDarkAura::instance() | |
388 : ui::NativeTheme::GetInstanceForNativeUi(); | |
389 return fallback_theme->GetSystemColor(color_id); | |
390 } | 335 } |
391 | 336 |
392 void NativeThemeGtk3::PaintMenuPopupBackground( | 337 void NativeThemeGtk3::PaintMenuPopupBackground( |
393 SkCanvas* canvas, | 338 SkCanvas* canvas, |
394 const gfx::Size& size, | 339 const gfx::Size& size, |
395 const MenuBackgroundExtraParams& menu_background) const { | 340 const MenuBackgroundExtraParams& menu_background) const { |
396 PaintWidget(canvas, gfx::Rect(size), "menu", GTK_STATE_FLAG_NORMAL); | 341 PaintWidget(canvas, gfx::Rect(size), "GtkMenu.menu", GTK_STATE_FLAG_NORMAL); |
397 } | 342 } |
398 | 343 |
399 void NativeThemeGtk3::PaintMenuItemBackground( | 344 void NativeThemeGtk3::PaintMenuItemBackground( |
400 SkCanvas* canvas, | 345 SkCanvas* canvas, |
401 State state, | 346 State state, |
402 const gfx::Rect& rect, | 347 const gfx::Rect& rect, |
403 const MenuItemExtraParams& menu_item) const { | 348 const MenuItemExtraParams& menu_item) const { |
404 PaintWidget(canvas, rect, "menu menuitem", StateToStateFlags(state)); | 349 PaintWidget(canvas, rect, "GtkMenu.menu GtkMenuItem.menuitem", |
405 } | 350 StateToStateFlags(state)); |
406 | |
407 GtkWidget* NativeThemeGtk3::GetWindow() const { | |
408 static GtkWidget* fake_window = NULL; | |
409 | |
410 if (!fake_window) { | |
411 fake_window = chrome_gtk_frame_new(); | |
412 gtk_widget_realize(fake_window); | |
413 } | |
414 | |
415 return fake_window; | |
416 } | |
417 | |
418 GtkWidget* NativeThemeGtk3::GetEntry() const { | |
419 static GtkWidget* fake_entry = NULL; | |
420 | |
421 if (!fake_entry) { | |
422 fake_entry = gtk_entry_new(); | |
423 | |
424 // The fake entry needs to be in the window so it can be realized so we can | |
425 // use the computed parts of the style. | |
426 gtk_container_add(GTK_CONTAINER(GetWindow()), fake_entry); | |
427 gtk_widget_realize(fake_entry); | |
428 } | |
429 | |
430 return fake_entry; | |
431 } | |
432 | |
433 GtkWidget* NativeThemeGtk3::GetLabel() const { | |
434 static GtkWidget* fake_label = NULL; | |
435 | |
436 if (!fake_label) | |
437 fake_label = gtk_label_new(""); | |
438 | |
439 return fake_label; | |
440 } | |
441 | |
442 GtkWidget* NativeThemeGtk3::GetButton() const { | |
443 static GtkWidget* fake_button = NULL; | |
444 | |
445 if (!fake_button) | |
446 fake_button = gtk_button_new(); | |
447 | |
448 return fake_button; | |
449 } | |
450 | |
451 GtkWidget* NativeThemeGtk3::GetBlueButton() const { | |
452 static GtkWidget* fake_bluebutton = NULL; | |
453 | |
454 if (!fake_bluebutton) { | |
455 fake_bluebutton = gtk_button_new(); | |
456 TurnButtonBlue(fake_bluebutton); | |
457 } | |
458 | |
459 return fake_bluebutton; | |
460 } | |
461 | |
462 GtkWidget* NativeThemeGtk3::GetTree() const { | |
463 static GtkWidget* fake_tree = NULL; | |
464 | |
465 if (!fake_tree) | |
466 fake_tree = gtk_tree_view_new(); | |
467 | |
468 return fake_tree; | |
469 } | |
470 | |
471 GtkWidget* NativeThemeGtk3::GetTooltip() const { | |
472 static GtkWidget* fake_tooltip = NULL; | |
473 | |
474 if (!fake_tooltip) { | |
475 fake_tooltip = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
476 gtk_widget_set_name(fake_tooltip, "gtk-tooltip"); | |
477 gtk_widget_realize(fake_tooltip); | |
478 } | |
479 | |
480 return fake_tooltip; | |
481 } | |
482 | |
483 GtkWidget* NativeThemeGtk3::GetMenu() const { | |
484 static GtkWidget* fake_menu = NULL; | |
485 | |
486 if (!fake_menu) | |
487 fake_menu = gtk_custom_menu_new(); | |
488 | |
489 return fake_menu; | |
490 } | |
491 | |
492 GtkWidget* NativeThemeGtk3::GetMenuItem() const { | |
493 static GtkWidget* fake_menu_item = NULL; | |
494 | |
495 if (!fake_menu_item) { | |
496 fake_menu_item = gtk_custom_menu_item_new(); | |
497 gtk_menu_shell_append(GTK_MENU_SHELL(GetMenu()), fake_menu_item); | |
498 } | |
499 | |
500 return fake_menu_item; | |
501 } | 351 } |
502 | 352 |
503 } // namespace libgtkui | 353 } // namespace libgtkui |
OLD | NEW |