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"
18 NSString* const kBrowserThemeDidChangeNotification =
19 @"BrowserThemeDidChangeNotification";
21 typedef ThemeProperties Properties;
25 void HSLToHSB(const color_utils::HSL& hsl, CGFloat* h, CGFloat* s, CGFloat* b) {
26 SkColor color = color_utils::HSLToSkColor(hsl, 255); // alpha doesn't matter
28 SkColorToHSV(color, hsv);
30 *h = SkScalarToDouble(hsv[0]) / 360.0;
31 *s = SkScalarToDouble(hsv[1]);
32 *b = SkScalarToDouble(hsv[2]);
37 NSImage* ThemeService::GetNSImageNamed(int id) const {
38 DCHECK(CalledOnValidThread());
40 // Check to see if we already have the image in the cache.
41 NSImageMap::const_iterator nsimage_iter = nsimage_cache_.find(id);
42 if (nsimage_iter != nsimage_cache_.end())
43 return nsimage_iter->second;
45 // Why don't we load the file directly into the image instead of the whole
46 // gfx::Image > native conversion?
47 // - For consistency with other platforms.
48 // - To get the generated tinted images.
49 NSImage* nsimage = nil;
50 if (theme_supplier_.get()) {
51 gfx::Image image = theme_supplier_->GetImageNamed(id);
53 nsimage = image.ToNSImage();
56 // If the theme didn't override this image then load it from the resource
59 nsimage = rb_.GetNativeImageNamed(id).ToNSImage();
62 // We loaded successfully. Cache the image.
64 nsimage_cache_[id] = [nsimage retain];
68 // We failed to retrieve the bitmap, show a debugging red square.
69 LOG(WARNING) << "Unable to load NSImage with id " << id;
70 NOTREACHED(); // Want to assert in debug mode.
72 static NSImage* empty_image = NULL;
74 // The placeholder image is bright red so people notice the problem. This
75 // image will be leaked, but this code should never be hit.
76 NSRect image_rect = NSMakeRect(0, 0, 32, 32);
77 empty_image = [[NSImage alloc] initWithSize:image_rect.size];
78 [empty_image lockFocus];
79 [[NSColor redColor] set];
80 NSRectFill(image_rect);
81 [empty_image unlockFocus];
87 NSColor* ThemeService::GetNSImageColorNamed(int id) const {
88 DCHECK(CalledOnValidThread());
90 // Check to see if we already have the color in the cache.
91 NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
92 if (nscolor_iter != nscolor_cache_.end())
93 return nscolor_iter->second;
95 NSImage* image = GetNSImageNamed(id);
98 NSColor* image_color = [NSColor colorWithPatternImage:image];
100 // We loaded successfully. Cache the color.
102 nscolor_cache_[id] = [image_color retain];
107 NSColor* ThemeService::GetNSColor(int id) const {
108 DCHECK(CalledOnValidThread());
110 // Check to see if we already have the color in the cache.
111 NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
112 if (nscolor_iter != nscolor_cache_.end())
113 return nscolor_iter->second;
115 SkColor sk_color = GetColor(id);
116 NSColor* color = gfx::SkColorToCalibratedNSColor(sk_color);
118 // We loaded successfully. Cache the color.
120 nscolor_cache_[id] = [color retain];
125 NSColor* ThemeService::GetNSColorTint(int id) const {
126 DCHECK(CalledOnValidThread());
128 // Check to see if we already have the color in the cache.
129 NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
130 if (nscolor_iter != nscolor_cache_.end())
131 return nscolor_iter->second;
133 color_utils::HSL tint = GetTint(id);
134 NSColor* tint_color = nil;
135 if (tint.h == -1 && tint.s == -1 && tint.l == -1) {
136 tint_color = [NSColor blackColor];
138 CGFloat hue, saturation, brightness;
139 HSLToHSB(tint, &hue, &saturation, &brightness);
141 tint_color = [NSColor colorWithCalibratedHue:hue
142 saturation:saturation
143 brightness:brightness
147 // We loaded successfully. Cache the color.
149 nscolor_cache_[id] = [tint_color retain];
154 NSGradient* ThemeService::GetNSGradient(int id) const {
155 DCHECK(CalledOnValidThread());
157 // Check to see if we already have the gradient in the cache.
158 NSGradientMap::const_iterator nsgradient_iter = nsgradient_cache_.find(id);
159 if (nsgradient_iter != nsgradient_cache_.end())
160 return nsgradient_iter->second;
162 NSGradient* gradient = nil;
164 // Note that we are not leaking when we assign a retained object to
165 // |gradient|; in all cases we cache it before we return.
167 case Properties::GRADIENT_FRAME_INCOGNITO:
168 case Properties::GRADIENT_FRAME_INCOGNITO_INACTIVE: {
169 // TODO(avi): can we simplify this?
170 BOOL active = id == Properties::GRADIENT_FRAME_INCOGNITO;
171 NSColor* base_color = [NSColor colorWithCalibratedRed:83/255.0
176 NSColor *start_color =
177 [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
180 [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
184 start_color = [start_color gtm_colorByAdjustingLuminance:0.1
186 end_color = [end_color gtm_colorByAdjustingLuminance:0.1
190 gradient = [[NSGradient alloc] initWithStartingColor:start_color
191 endingColor:end_color];
195 case Properties::GRADIENT_TOOLBAR:
196 case Properties::GRADIENT_TOOLBAR_INACTIVE: {
197 NSColor* base_color = [NSColor colorWithCalibratedWhite:0.2 alpha:1.0];
198 BOOL faded = (id == Properties::GRADIENT_TOOLBAR_INACTIVE ) ||
199 (id == Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE);
200 NSColor* start_color =
201 [base_color gtm_colorAdjustedFor:GTMColorationLightHighlight
204 [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone
207 [base_color gtm_colorAdjustedFor:GTMColorationLightShadow
209 NSColor* glow_color =
210 [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra
214 [[NSGradient alloc] initWithColorsAndLocations:start_color, 0.0,
222 case Properties::GRADIENT_TOOLBAR_BUTTON:
223 case Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE: {
224 NSColor* start_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.0];
225 NSColor* end_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.3];
226 gradient = [[NSGradient alloc] initWithStartingColor:start_color
227 endingColor:end_color];
230 case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED:
231 case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE: {
232 NSColor* base_color = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0];
233 BOOL faded = id == Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE;
234 NSColor* start_color =
235 [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
238 [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
241 gradient = [[NSGradient alloc] initWithStartingColor:start_color
242 endingColor:end_color];
246 LOG(WARNING) << "Gradient request with unknown id " << id;
247 NOTREACHED(); // Want to assert in debug mode.
251 // We loaded successfully. Cache the gradient.
253 nsgradient_cache_[id] = gradient; // created retained
258 // Let all the browser views know that themes have changed in a platform way.
259 void ThemeService::NotifyPlatformThemeChanged() {
260 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
261 [defaultCenter postNotificationName:kBrowserThemeDidChangeNotification
262 object:[NSValue valueWithPointer:this]];
265 void ThemeService::FreePlatformCaches() {
266 DCHECK(CalledOnValidThread());
269 for (NSImageMap::iterator i = nsimage_cache_.begin();
270 i != nsimage_cache_.end(); i++) {
273 nsimage_cache_.clear();
276 for (NSColorMap::iterator i = nscolor_cache_.begin();
277 i != nscolor_cache_.end(); i++) {
280 nscolor_cache_.clear();
283 for (NSGradientMap::iterator i = nsgradient_cache_.begin();
284 i != nsgradient_cache_.end(); i++) {
287 nsgradient_cache_.clear();