1 // Copyright (c) 2011 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.
5 #include "chrome/browser/themes/theme_service.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/logging.h"
10 #include "chrome/browser/themes/browser_theme_pack.h"
11 #include "chrome/browser/themes/theme_properties.h"
12 #include "skia/ext/skia_utils_mac.h"
13 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSColor+Luminance.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/gfx/color_utils.h"
16 #include "ui/gfx/image/image.h"
17 #include "skia/ext/skia_utils_mac.h"
19 NSString* const kBrowserThemeDidChangeNotification =
20 @"BrowserThemeDidChangeNotification";
22 typedef ThemeProperties Properties;
26 void HSLToHSB(const color_utils::HSL& hsl, CGFloat* h, CGFloat* s, CGFloat* b) {
27 SkColor color = color_utils::HSLToSkColor(hsl, 255); // alpha doesn't matter
29 SkColorToHSV(color, hsv);
31 *h = SkScalarToDouble(hsv[0]) / 360.0;
32 *s = SkScalarToDouble(hsv[1]);
33 *b = SkScalarToDouble(hsv[2]);
38 NSImage* ThemeService::GetNSImageNamed(int id) const {
39 DCHECK(CalledOnValidThread());
41 // Check to see if we already have the image in the cache.
42 NSImageMap::const_iterator nsimage_iter = nsimage_cache_.find(id);
43 if (nsimage_iter != nsimage_cache_.end())
44 return nsimage_iter->second;
46 // Why don't we load the file directly into the image instead of the whole
47 // gfx::Image > native conversion?
48 // - For consistency with other platforms.
49 // - To get the generated tinted images.
50 NSImage* nsimage = nil;
51 if (theme_supplier_.get()) {
52 gfx::Image image = theme_supplier_->GetImageNamed(id);
54 nsimage = image.ToNSImage();
57 // If the theme didn't override this image then load it from the resource
60 nsimage = rb_.GetNativeImageNamed(id).ToNSImage();
63 // We loaded successfully. Cache the image.
65 nsimage_cache_[id] = [nsimage retain];
69 // We failed to retrieve the bitmap, show a debugging red square.
70 LOG(WARNING) << "Unable to load NSImage with id " << id;
71 NOTREACHED(); // Want to assert in debug mode.
73 static NSImage* empty_image = NULL;
75 // The placeholder image is bright red so people notice the problem. This
76 // image will be leaked, but this code should never be hit.
77 NSRect image_rect = NSMakeRect(0, 0, 32, 32);
78 empty_image = [[NSImage alloc] initWithSize:image_rect.size];
79 [empty_image lockFocus];
80 [[NSColor redColor] set];
81 NSRectFill(image_rect);
82 [empty_image unlockFocus];
88 NSColor* ThemeService::GetNSImageColorNamed(int id) const {
89 DCHECK(CalledOnValidThread());
91 // Check to see if we already have the color in the cache.
92 NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
93 if (nscolor_iter != nscolor_cache_.end())
94 return nscolor_iter->second;
96 NSImage* image = GetNSImageNamed(id);
99 NSColor* image_color = [NSColor colorWithPatternImage:image];
101 // We loaded successfully. Cache the color.
103 nscolor_cache_[id] = [image_color retain];
108 NSColor* ThemeService::GetNSColor(int id) const {
109 DCHECK(CalledOnValidThread());
111 // Check to see if we already have the color in the cache.
112 NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
113 if (nscolor_iter != nscolor_cache_.end())
114 return nscolor_iter->second;
116 SkColor sk_color = GetColor(id);
117 NSColor* color = gfx::SkColorToCalibratedNSColor(sk_color);
119 // We loaded successfully. Cache the color.
121 nscolor_cache_[id] = [color retain];
126 NSColor* ThemeService::GetNSColorTint(int id) const {
127 DCHECK(CalledOnValidThread());
129 // Check to see if we already have the color in the cache.
130 NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
131 if (nscolor_iter != nscolor_cache_.end())
132 return nscolor_iter->second;
134 color_utils::HSL tint = GetTint(id);
135 NSColor* tint_color = nil;
136 if (tint.h == -1 && tint.s == -1 && tint.l == -1) {
137 tint_color = [NSColor blackColor];
139 CGFloat hue, saturation, brightness;
140 HSLToHSB(tint, &hue, &saturation, &brightness);
142 tint_color = [NSColor colorWithCalibratedHue:hue
143 saturation:saturation
144 brightness:brightness
148 // We loaded successfully. Cache the color.
150 nscolor_cache_[id] = [tint_color retain];
155 NSGradient* ThemeService::GetNSGradient(int id) const {
156 DCHECK(CalledOnValidThread());
158 // Check to see if we already have the gradient in the cache.
159 NSGradientMap::const_iterator nsgradient_iter = nsgradient_cache_.find(id);
160 if (nsgradient_iter != nsgradient_cache_.end())
161 return nsgradient_iter->second;
163 NSGradient* gradient = nil;
165 // Note that we are not leaking when we assign a retained object to
166 // |gradient|; in all cases we cache it before we return.
168 case Properties::GRADIENT_FRAME_INCOGNITO:
169 case Properties::GRADIENT_FRAME_INCOGNITO_INACTIVE: {
170 // TODO(avi): can we simplify this?
171 BOOL active = id == Properties::GRADIENT_FRAME_INCOGNITO;
172 NSColor* base_color = [NSColor colorWithCalibratedRed:83/255.0
177 NSColor *start_color =
178 [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
181 [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
185 start_color = [start_color gtm_colorByAdjustingLuminance:0.1
187 end_color = [end_color gtm_colorByAdjustingLuminance:0.1
191 gradient = [[NSGradient alloc] initWithStartingColor:start_color
192 endingColor:end_color];
196 case Properties::GRADIENT_TOOLBAR:
197 case Properties::GRADIENT_TOOLBAR_INACTIVE: {
198 NSColor* base_color = [NSColor colorWithCalibratedWhite:0.2 alpha:1.0];
199 BOOL faded = (id == Properties::GRADIENT_TOOLBAR_INACTIVE ) ||
200 (id == Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE);
201 NSColor* start_color =
202 [base_color gtm_colorAdjustedFor:GTMColorationLightHighlight
205 [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone
208 [base_color gtm_colorAdjustedFor:GTMColorationLightShadow
210 NSColor* glow_color =
211 [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra
215 [[NSGradient alloc] initWithColorsAndLocations:start_color, 0.0,
223 case Properties::GRADIENT_TOOLBAR_BUTTON:
224 case Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE: {
225 NSColor* start_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.0];
226 NSColor* end_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.3];
227 gradient = [[NSGradient alloc] initWithStartingColor:start_color
228 endingColor:end_color];
231 case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED:
232 case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE: {
233 NSColor* base_color = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0];
234 BOOL faded = id == Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE;
235 NSColor* start_color =
236 [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
239 [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
242 gradient = [[NSGradient alloc] initWithStartingColor:start_color
243 endingColor:end_color];
247 LOG(WARNING) << "Gradient request with unknown id " << id;
248 NOTREACHED(); // Want to assert in debug mode.
252 // We loaded successfully. Cache the gradient.
254 nsgradient_cache_[id] = gradient; // created retained
259 // Let all the browser views know that themes have changed in a platform way.
260 void ThemeService::NotifyPlatformThemeChanged() {
261 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
262 [defaultCenter postNotificationName:kBrowserThemeDidChangeNotification
263 object:[NSValue valueWithPointer:this]];
266 void ThemeService::FreePlatformCaches() {
267 DCHECK(CalledOnValidThread());
270 for (NSImageMap::iterator i = nsimage_cache_.begin();
271 i != nsimage_cache_.end(); i++) {
274 nsimage_cache_.clear();
277 for (NSColorMap::iterator i = nscolor_cache_.begin();
278 i != nscolor_cache_.end(); i++) {
281 nscolor_cache_.clear();
284 for (NSGradientMap::iterator i = nsgradient_cache_.begin();
285 i != nsgradient_cache_.end(); i++) {
288 nsgradient_cache_.clear();