forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnative_theme_mac.mm
292 lines (257 loc) · 11.6 KB
/
native_theme_mac.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/native_theme/native_theme_mac.h"
#import <Cocoa/Cocoa.h>
#include <stddef.h>
#include "base/mac/mac_util.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/macros.h"
#import "skia/ext/skia_utils_mac.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/skia_util.h"
#include "ui/native_theme/common_theme.h"
namespace {
// Values calculated by reading pixels and solving simultaneous equations
// derived from "A over B" alpha compositing. Steps: Sample the semi-transparent
// pixel over two backgrounds; P1, P2 over backgrounds B1, B2. Use the color
// value between 0.0 and 1.0 (i.e. divide by 255.0). Then,
// alpha = (P2 - P1 + B1 - B2) / (B1 - B2)
// color = (P1 - B1 + alpha * B1) / alpha.
const SkColor kMenuPopupBackgroundColor = SkColorSetARGB(245, 255, 255, 255);
const SkColor kMenuSeparatorColor = SkColorSetARGB(255, 217, 217, 217);
const SkColor kMenuBorderColor = SkColorSetARGB(60, 0, 0, 0);
const SkColor kMenuPopupBackgroundColorMavericks =
SkColorSetARGB(255, 255, 255, 255);
const SkColor kMenuSeparatorColorMavericks = SkColorSetARGB(243, 228, 228, 228);
// Hardcoded color used for some existing dialogs in Chrome's Cocoa UI.
const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
// Color for the highlighted text in a control when that control doesn't have
// keyboard focus.
const SkColor kUnfocusedSelectedTextBackgroundColor =
SkColorSetRGB(220, 220, 220);
// Helper to make indexing an array by an enum class easier.
template <class KEY, class VALUE>
struct EnumArray {
VALUE& operator[](const KEY& key) { return array[static_cast<size_t>(key)]; }
VALUE array[static_cast<size_t>(KEY::COUNT)];
};
// NSColor has a number of methods that return system colors (i.e. controlled by
// user preferences). This function converts the color given by an NSColor class
// method to an SkColor. Official documentation suggests developers only rely on
// +[NSColor selectedTextBackgroundColor] and +[NSColor selectedControlColor],
// but other colors give a good baseline. For many, a gradient is involved; the
// palette chosen based on the enum value given by +[NSColor currentColorTint].
// Apple's documentation also suggests to use NSColorList, but the system color
// list is just populated with class methods on NSColor.
SkColor NSSystemColorToSkColor(NSColor* color) {
// System colors use the an NSNamedColorSpace called "System", so first step
// is to convert the color into something that can be worked with.
NSColor* device_color =
[color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
if (device_color)
return skia::NSDeviceColorToSkColor(device_color);
// Sometimes the conversion is not possible, but we can get an approximation
// by going through a CGColorRef. Note that simply using NSColor methods for
// accessing components for system colors results in exceptions like
// "-numberOfComponents not valid for the NSColor NSNamedColorSpace System
// windowBackgroundColor; need to first convert colorspace." Hence the
// conversion first to CGColor.
CGColorRef cg_color = [color CGColor];
const size_t component_count = CGColorGetNumberOfComponents(cg_color);
if (component_count == 4)
return skia::CGColorRefToSkColor(cg_color);
CHECK(component_count == 1 || component_count == 2);
// 1-2 components means a grayscale channel and maybe an alpha channel, which
// CGColorRefToSkColor will not like. But RGB is additive, so the conversion
// is easy (RGB to grayscale is less easy).
const CGFloat* components = CGColorGetComponents(cg_color);
CGFloat alpha = component_count == 2 ? components[1] : 1.0;
return SkColorSetARGB(SkScalarRoundToInt(255.0 * alpha),
SkScalarRoundToInt(255.0 * components[0]),
SkScalarRoundToInt(255.0 * components[0]),
SkScalarRoundToInt(255.0 * components[0]));
}
// Converts an SkColor to grayscale by using luminance for all three components.
// Experimentally, this seems to produce a better result than a flat average or
// a min/max average for UI controls.
SkColor ColorToGrayscale(SkColor color) {
SkScalar luminance = SkColorGetR(color) * 0.21 +
SkColorGetG(color) * 0.72 +
SkColorGetB(color) * 0.07;
uint8_t component = SkScalarRoundToInt(luminance);
return SkColorSetARGB(SkColorGetA(color), component, component, component);
}
} // namespace
namespace ui {
// static
NativeTheme* NativeTheme::GetInstanceForWeb() {
return NativeThemeMac::instance();
}
// static
NativeTheme* NativeTheme::GetInstanceForNativeUi() {
return NativeThemeMac::instance();
}
// static
NativeThemeMac* NativeThemeMac::instance() {
CR_DEFINE_STATIC_LOCAL(NativeThemeMac, s_native_theme, ());
return &s_native_theme;
}
// static
SkColor NativeThemeMac::ApplySystemControlTint(SkColor color) {
if ([NSColor currentControlTint] == NSGraphiteControlTint)
return ColorToGrayscale(color);
return color;
}
SkColor NativeThemeMac::GetSystemColor(ColorId color_id) const {
// Even with --secondary-ui-md, menus use the platform colors and styling, and
// Mac has a couple of specific color overrides, documented below.
switch (color_id) {
case kColorId_EnabledMenuItemForegroundColor:
return NSSystemColorToSkColor([NSColor controlTextColor]);
case kColorId_DisabledMenuItemForegroundColor:
return NSSystemColorToSkColor([NSColor disabledControlTextColor]);
case kColorId_SelectedMenuItemForegroundColor:
return NSSystemColorToSkColor([NSColor selectedMenuItemTextColor]);
case kColorId_FocusedMenuItemBackgroundColor:
return NSSystemColorToSkColor([NSColor selectedMenuItemColor]);
case kColorId_MenuBackgroundColor:
return kMenuPopupBackgroundColor;
case kColorId_MenuSeparatorColor:
return base::mac::IsOS10_9() ? kMenuSeparatorColorMavericks
: kMenuSeparatorColor;
case kColorId_MenuBorderColor:
return kMenuBorderColor;
// Mac has a different "pressed button" styling because it doesn't use
// ripples.
case kColorId_ButtonPressedShade:
return SkColorSetA(SK_ColorBLACK, 0x10);
// There's a system setting General > Highlight color which sets the
// background color for text selections. We honor that setting.
// TODO(ellyjones): Listen for NSSystemColorsDidChangeNotification somewhere
// and propagate it to the View hierarchy.
case kColorId_LabelTextSelectionBackgroundFocused:
case kColorId_TextfieldSelectionBackgroundFocused:
return NSSystemColorToSkColor([NSColor selectedTextBackgroundColor]);
default:
break;
}
if (ui::MaterialDesignController::IsSecondaryUiMaterial())
return ApplySystemControlTint(GetAuraColor(color_id, this));
// TODO(tapted): Add caching for these, and listen for
// NSSystemColorsDidChangeNotification.
switch (color_id) {
case kColorId_WindowBackground:
return NSSystemColorToSkColor([NSColor windowBackgroundColor]);
case kColorId_DialogBackground:
return kDialogBackgroundColor;
case kColorId_BubbleBackground:
return SK_ColorWHITE;
case kColorId_FocusedBorderColor:
return NSSystemColorToSkColor([NSColor keyboardFocusIndicatorColor]);
case kColorId_UnfocusedBorderColor:
return NSSystemColorToSkColor([NSColor controlColor]);
// Buttons and labels.
case kColorId_ButtonEnabledColor:
case kColorId_LabelEnabledColor:
return NSSystemColorToSkColor([NSColor controlTextColor]);
// NSColor doesn't offer a color for prominent buttons. Use the Aura color,
// but apply the system tint. This is a good match for the blue Cocoa uses
// to draw buttons that are given a \n key equivalent.
case kColorId_ProminentButtonColor:
return ApplySystemControlTint(GetAuraColor(color_id, this));
case kColorId_ButtonDisabledColor:
case kColorId_LabelDisabledColor:
return NSSystemColorToSkColor([NSColor disabledControlTextColor]);
case kColorId_ButtonHoverColor:
return NSSystemColorToSkColor([NSColor selectedControlTextColor]);
case kColorId_LabelTextSelectionColor:
return NSSystemColorToSkColor([NSColor selectedTextColor]);
// Link.
case kColorId_LinkDisabled:
return SK_ColorBLACK;
case kColorId_LinkEnabled:
return SK_ColorBLUE;
case kColorId_LinkPressed:
return SK_ColorRED;
// Text fields.
case kColorId_TextfieldDefaultColor:
case kColorId_TextfieldReadOnlyColor:
return NSSystemColorToSkColor([NSColor textColor]);
case kColorId_TextfieldDefaultBackground:
case kColorId_TextfieldReadOnlyBackground:
return NSSystemColorToSkColor([NSColor textBackgroundColor]);
case kColorId_TextfieldSelectionColor:
return NSSystemColorToSkColor([NSColor selectedTextColor]);
// Trees/Tables. For focused text, use the alternate* versions, which
// NSColor documents as "the table and list view equivalent to the
// selectedControlTextColor".
case kColorId_TreeBackground:
case kColorId_TableBackground:
return NSSystemColorToSkColor([NSColor controlBackgroundColor]);
case kColorId_TreeText:
case kColorId_TableText:
case kColorId_TableSelectedTextUnfocused:
case kColorId_TreeSelectedTextUnfocused:
return NSSystemColorToSkColor([NSColor textColor]);
case kColorId_TreeSelectedText:
case kColorId_TableSelectedText:
return NSSystemColorToSkColor(
[NSColor alternateSelectedControlTextColor]);
case kColorId_TreeSelectionBackgroundFocused:
case kColorId_TableSelectionBackgroundFocused:
return NSSystemColorToSkColor([NSColor alternateSelectedControlColor]);
case kColorId_TreeSelectionBackgroundUnfocused:
case kColorId_TableSelectionBackgroundUnfocused:
return kUnfocusedSelectedTextBackgroundColor;
case kColorId_TableGroupingIndicatorColor:
return SkColorSetRGB(140, 140, 140);
default:
// TODO(tapted): Handle all values and remove the default case.
return GetAuraColor(color_id, this);
}
}
void NativeThemeMac::PaintMenuPopupBackground(
cc::PaintCanvas* canvas,
const gfx::Size& size,
const MenuBackgroundExtraParams& menu_background) const {
cc::PaintFlags flags;
flags.setAntiAlias(true);
if (base::mac::IsOS10_9())
flags.setColor(kMenuPopupBackgroundColorMavericks);
else
flags.setColor(kMenuPopupBackgroundColor);
const SkScalar radius = SkIntToScalar(menu_background.corner_radius);
SkRect rect = gfx::RectToSkRect(gfx::Rect(size));
canvas->drawRoundRect(rect, radius, radius, flags);
}
void NativeThemeMac::PaintMenuItemBackground(
cc::PaintCanvas* canvas,
State state,
const gfx::Rect& rect,
const MenuItemExtraParams& menu_item) const {
cc::PaintFlags flags;
switch (state) {
case NativeTheme::kNormal:
case NativeTheme::kDisabled:
// Draw nothing over the regular background.
break;
case NativeTheme::kHovered:
// TODO(tapted): Draw a gradient, and use [NSColor currentControlTint] to
// pick colors. The System color "selectedMenuItemColor" is actually still
// blue for Graphite. And while "keyboardFocusIndicatorColor" does change,
// and is a good shade of gray, it's not blue enough for the Blue theme.
flags.setColor(GetSystemColor(kColorId_FocusedMenuItemBackgroundColor));
canvas->drawRect(gfx::RectToSkRect(rect), flags);
break;
default:
NOTREACHED();
break;
}
}
NativeThemeMac::NativeThemeMac() {
}
NativeThemeMac::~NativeThemeMac() {
}
} // namespace ui