Home | History | Annotate | Download | only in location_bar
      1 // Copyright (c) 2010 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 <cmath>
      6 
      7 #import "chrome/browser/ui/cocoa/location_bar/bubble_decoration.h"
      8 
      9 #include "base/logging.h"
     10 #import "chrome/browser/ui/cocoa/image_utils.h"
     11 
     12 namespace {
     13 
     14 // Padding between the icon/label and bubble edges.
     15 const CGFloat kBubblePadding = 3.0;
     16 
     17 // The image needs to be in the same position as for the location
     18 // icon, which implies that the bubble's padding in the Omnibox needs
     19 // to differ from the location icon's.  Indeed, that's how the views
     20 // implementation handles the problem.  This draws the bubble edge a
     21 // little bit further left, which is easier but no less hacky.
     22 const CGFloat kLeftSideOverdraw = 2.0;
     23 
     24 // Omnibox corner radius is |4.0|, this needs to look tight WRT that.
     25 const CGFloat kBubbleCornerRadius = 2.0;
     26 
     27 // How far to inset the bubble from the top and bottom of the drawing
     28 // frame.
     29 // TODO(shess): Would be nicer to have the drawing code factor out the
     30 // space outside the border, and perhaps the border.  Then this could
     31 // reflect the single pixel space w/in that.
     32 const CGFloat kBubbleYInset = 4.0;
     33 
     34 }  // namespace
     35 
     36 BubbleDecoration::BubbleDecoration(NSFont* font) {
     37   DCHECK(font);
     38   if (font) {
     39     NSDictionary* attributes =
     40         [NSDictionary dictionaryWithObject:font
     41                                     forKey:NSFontAttributeName];
     42     attributes_.reset([attributes retain]);
     43   }
     44 }
     45 
     46 BubbleDecoration::~BubbleDecoration() {
     47 }
     48 
     49 CGFloat BubbleDecoration::GetWidthForImageAndLabel(NSImage* image,
     50                                                    NSString* label) {
     51   if (!image && !label)
     52     return kOmittedWidth;
     53 
     54   const CGFloat image_width = image ? [image size].width : 0.0;
     55   if (!label)
     56     return kBubblePadding + image_width;
     57 
     58   // The bubble needs to take up an integral number of pixels.
     59   // Generally -sizeWithAttributes: seems to overestimate rather than
     60   // underestimate, so floor() seems to work better.
     61   const CGFloat label_width =
     62       std::floor([label sizeWithAttributes:attributes_].width);
     63   return kBubblePadding + image_width + label_width;
     64 }
     65 
     66 NSRect BubbleDecoration::GetImageRectInFrame(NSRect frame) {
     67   NSRect imageRect = NSInsetRect(frame, 0.0, kBubbleYInset);
     68   if (image_) {
     69     // Center the image vertically.
     70     const NSSize imageSize = [image_ size];
     71     imageRect.origin.y +=
     72         std::floor((NSHeight(frame) - imageSize.height) / 2.0);
     73     imageRect.size = imageSize;
     74   }
     75   return imageRect;
     76 }
     77 
     78 CGFloat BubbleDecoration::GetWidthForSpace(CGFloat width) {
     79   const CGFloat all_width = GetWidthForImageAndLabel(image_, label_);
     80   if (all_width <= width)
     81     return all_width;
     82 
     83   const CGFloat image_width = GetWidthForImageAndLabel(image_, nil);
     84   if (image_width <= width)
     85     return image_width;
     86 
     87   return kOmittedWidth;
     88 }
     89 
     90 void BubbleDecoration::DrawInFrame(NSRect frame, NSView* control_view) {
     91   const NSRect decorationFrame = NSInsetRect(frame, 0.0, kBubbleYInset);
     92 
     93   // The inset is to put the border down the middle of the pixel.
     94   NSRect bubbleFrame = NSInsetRect(decorationFrame, 0.5, 0.5);
     95   bubbleFrame.origin.x -= kLeftSideOverdraw;
     96   bubbleFrame.size.width += kLeftSideOverdraw;
     97   NSBezierPath* path =
     98       [NSBezierPath bezierPathWithRoundedRect:bubbleFrame
     99                                       xRadius:kBubbleCornerRadius
    100                                       yRadius:kBubbleCornerRadius];
    101 
    102   [background_color_ setFill];
    103   [path fill];
    104 
    105   [border_color_ setStroke];
    106   [path setLineWidth:1.0];
    107   [path stroke];
    108 
    109   NSRect imageRect = decorationFrame;
    110   if (image_) {
    111     // Center the image vertically.
    112     const NSSize imageSize = [image_ size];
    113     imageRect.origin.y +=
    114         std::floor((NSHeight(decorationFrame) - imageSize.height) / 2.0);
    115     imageRect.size = imageSize;
    116     [image_ drawInRect:imageRect
    117               fromRect:NSZeroRect  // Entire image
    118              operation:NSCompositeSourceOver
    119               fraction:1.0
    120           neverFlipped:YES];
    121   } else {
    122     imageRect.size = NSZeroSize;
    123   }
    124 
    125   if (label_) {
    126     NSRect textRect = decorationFrame;
    127     textRect.origin.x = NSMaxX(imageRect);
    128     textRect.size.width = NSMaxX(decorationFrame) - NSMinX(textRect);
    129     [label_ drawInRect:textRect withAttributes:attributes_];
    130   }
    131 }
    132 
    133 NSImage* BubbleDecoration::GetImage() {
    134   return image_;
    135 }
    136 
    137 void BubbleDecoration::SetImage(NSImage* image) {
    138   image_.reset([image retain]);
    139 }
    140 
    141 void BubbleDecoration::SetLabel(NSString* label) {
    142   // If the initializer was called with |nil|, then the code cannot
    143   // process a label.
    144   DCHECK(attributes_);
    145   if (attributes_)
    146     label_.reset([label copy]);
    147 }
    148 
    149 void BubbleDecoration::SetColors(NSColor* border_color,
    150                                  NSColor* background_color,
    151                                  NSColor* text_color) {
    152   border_color_.reset([border_color retain]);
    153   background_color_.reset([background_color retain]);
    154 
    155   scoped_nsobject<NSMutableDictionary> attributes([attributes_ mutableCopy]);
    156   [attributes setObject:text_color forKey:NSForegroundColorAttributeName];
    157   attributes_.reset([attributes copy]);
    158 }
    159