Home | History | Annotate | Download | only in themes
      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.
      4 
      5 #include "chrome/browser/themes/theme_service.h"
      6 
      7 #import <Cocoa/Cocoa.h>
      8 
      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/GTM/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"
     18 
     19 NSString* const kBrowserThemeDidChangeNotification =
     20     @"BrowserThemeDidChangeNotification";
     21 
     22 typedef ThemeProperties Properties;
     23 
     24 namespace {
     25 
     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
     28   SkScalar hsv[3];
     29   SkColorToHSV(color, hsv);
     30 
     31   *h = SkScalarToDouble(hsv[0]) / 360.0;
     32   *s = SkScalarToDouble(hsv[1]);
     33   *b = SkScalarToDouble(hsv[2]);
     34 }
     35 
     36 }  // namespace
     37 
     38 NSImage* ThemeService::GetNSImageNamed(int id) const {
     39   DCHECK(CalledOnValidThread());
     40 
     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;
     45 
     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);
     53     if (!image.IsEmpty())
     54       nsimage = image.ToNSImage();
     55   }
     56 
     57   // If the theme didn't override this image then load it from the resource
     58   // bundle.
     59   if (!nsimage) {
     60     nsimage = rb_.GetNativeImageNamed(id).ToNSImage();
     61   }
     62 
     63   // We loaded successfully.  Cache the image.
     64   if (nsimage) {
     65     nsimage_cache_[id] = [nsimage retain];
     66     return nsimage;
     67   }
     68 
     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.
     72 
     73   static NSImage* empty_image = NULL;
     74   if (!empty_image) {
     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];
     83   }
     84 
     85   return empty_image;
     86 }
     87 
     88 NSColor* ThemeService::GetNSImageColorNamed(int id) const {
     89   DCHECK(CalledOnValidThread());
     90 
     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;
     95 
     96   NSImage* image = GetNSImageNamed(id);
     97   if (!image)
     98     return nil;
     99   NSColor* image_color = [NSColor colorWithPatternImage:image];
    100 
    101   // We loaded successfully.  Cache the color.
    102   if (image_color)
    103     nscolor_cache_[id] = [image_color retain];
    104 
    105   return image_color;
    106 }
    107 
    108 NSColor* ThemeService::GetNSColor(int id) const {
    109   DCHECK(CalledOnValidThread());
    110 
    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;
    115 
    116   SkColor sk_color = GetColor(id);
    117   NSColor* color = gfx::SkColorToCalibratedNSColor(sk_color);
    118 
    119   // We loaded successfully.  Cache the color.
    120   if (color)
    121     nscolor_cache_[id] = [color retain];
    122 
    123   return color;
    124 }
    125 
    126 NSColor* ThemeService::GetNSColorTint(int id) const {
    127   DCHECK(CalledOnValidThread());
    128 
    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;
    133 
    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];
    138   } else {
    139     CGFloat hue, saturation, brightness;
    140     HSLToHSB(tint, &hue, &saturation, &brightness);
    141 
    142     tint_color = [NSColor colorWithCalibratedHue:hue
    143                                       saturation:saturation
    144                                       brightness:brightness
    145                                            alpha:1.0];
    146   }
    147 
    148   // We loaded successfully.  Cache the color.
    149   if (tint_color)
    150     nscolor_cache_[id] = [tint_color retain];
    151 
    152   return tint_color;
    153 }
    154 
    155 NSGradient* ThemeService::GetNSGradient(int id) const {
    156   DCHECK(CalledOnValidThread());
    157 
    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;
    162 
    163   NSGradient* gradient = nil;
    164 
    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.
    167   switch (id) {
    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
    173                                                       green:108.0/255.0
    174                                                        blue:140/255.0
    175                                                       alpha:1.0];
    176 
    177       NSColor *start_color =
    178           [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
    179                                      faded:!active];
    180       NSColor *end_color =
    181           [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
    182                                      faded:!active];
    183 
    184       if (!active) {
    185         start_color = [start_color gtm_colorByAdjustingLuminance:0.1
    186                                                       saturation:0.5];
    187         end_color = [end_color gtm_colorByAdjustingLuminance:0.1
    188                                                   saturation:0.5];
    189       }
    190 
    191       gradient = [[NSGradient alloc] initWithStartingColor:start_color
    192                                                endingColor:end_color];
    193       break;
    194     }
    195 
    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
    203                                      faded:faded];
    204       NSColor* mid_color =
    205           [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone
    206                                      faded:faded];
    207       NSColor* end_color =
    208           [base_color gtm_colorAdjustedFor:GTMColorationLightShadow
    209                                      faded:faded];
    210       NSColor* glow_color =
    211           [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra
    212                                      faded:faded];
    213 
    214       gradient =
    215           [[NSGradient alloc] initWithColorsAndLocations:start_color, 0.0,
    216                                                          mid_color, 0.25,
    217                                                          end_color, 0.5,
    218                                                          glow_color, 0.75,
    219                                                          nil];
    220       break;
    221     }
    222 
    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];
    229       break;
    230     }
    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
    237                                      faded:faded];
    238       NSColor* end_color =
    239           [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
    240                                      faded:faded];
    241 
    242       gradient = [[NSGradient alloc] initWithStartingColor:start_color
    243                                                endingColor:end_color];
    244       break;
    245     }
    246     default:
    247       LOG(WARNING) << "Gradient request with unknown id " << id;
    248       NOTREACHED();  // Want to assert in debug mode.
    249       break;
    250   }
    251 
    252   // We loaded successfully.  Cache the gradient.
    253   if (gradient)
    254     nsgradient_cache_[id] = gradient;  // created retained
    255 
    256   return gradient;
    257 }
    258 
    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]];
    264 }
    265 
    266 void ThemeService::FreePlatformCaches() {
    267   DCHECK(CalledOnValidThread());
    268 
    269   // Free images.
    270   for (NSImageMap::iterator i = nsimage_cache_.begin();
    271        i != nsimage_cache_.end(); i++) {
    272     [i->second release];
    273   }
    274   nsimage_cache_.clear();
    275 
    276   // Free colors.
    277   for (NSColorMap::iterator i = nscolor_cache_.begin();
    278        i != nscolor_cache_.end(); i++) {
    279     [i->second release];
    280   }
    281   nscolor_cache_.clear();
    282 
    283   // Free gradients.
    284   for (NSGradientMap::iterator i = nsgradient_cache_.begin();
    285        i != nsgradient_cache_.end(); i++) {
    286     [i->second release];
    287   }
    288   nsgradient_cache_.clear();
    289 }
    290