blob: 155c42f502066d7cf3db82f98c23114b0ccc4f62 [file] [log] [blame]
// Copyright (c) 2010 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 <cmath>
#import "chrome/browser/ui/cocoa/location_bar/bubble_decoration.h"
#include "base/logging.h"
namespace {
// Padding between the icon/label and bubble edges.
const CGFloat kBubblePadding = 3.0;
// The image needs to be in the same position as for the location
// icon, which implies that the bubble's padding in the Omnibox needs
// to differ from the location icon's. Indeed, that's how the views
// implementation handles the problem. This draws the bubble edge a
// little bit further left, which is easier but no less hacky.
const CGFloat kLeftSideOverdraw = 2.0;
// Omnibox corner radius is |4.0|, this needs to look tight WRT that.
const CGFloat kBubbleCornerRadius = 2.0;
// Padding between the icon and label.
const CGFloat kIconLabelPadding = 4.0;
// Inset for the background.
const CGFloat kBackgroundYInset = 4.0;
} // namespace
BubbleDecoration::BubbleDecoration() {
attributes_.reset([[NSMutableDictionary alloc] init]);
[attributes_ setObject:GetFont() forKey:NSFontAttributeName];
}
BubbleDecoration::~BubbleDecoration() {
}
CGFloat BubbleDecoration::GetWidthForImageAndLabel(NSImage* image,
NSString* label) {
if (!image && !label)
return kOmittedWidth;
const CGFloat image_width = image ? [image size].width : 0.0;
if (!label)
return kBubblePadding + image_width;
// The bubble needs to take up an integral number of pixels.
// Generally -sizeWithAttributes: seems to overestimate rather than
// underestimate, so floor() seems to work better.
const CGFloat label_width =
std::floor([label sizeWithAttributes:attributes_].width);
return kBubblePadding + image_width + kIconLabelPadding + label_width;
}
NSRect BubbleDecoration::GetImageRectInFrame(NSRect frame) {
NSRect imageRect = NSInsetRect(frame, 0.0, kBackgroundYInset);
if (image_) {
// Center the image vertically.
const NSSize imageSize = [image_ size];
imageRect.origin.y +=
std::floor((NSHeight(frame) - imageSize.height) / 2.0);
imageRect.size = imageSize;
}
return imageRect;
}
CGFloat BubbleDecoration::GetWidthForSpace(CGFloat width) {
const CGFloat all_width = GetWidthForImageAndLabel(image_, label_);
if (all_width <= width)
return all_width;
const CGFloat image_width = GetWidthForImageAndLabel(image_, nil);
if (image_width <= width)
return image_width;
return kOmittedWidth;
}
void BubbleDecoration::DrawInFrame(NSRect frame, NSView* control_view) {
const NSRect decorationFrame = NSInsetRect(frame, 0.0, kBackgroundYInset);
// The inset is to put the border down the middle of the pixel.
NSRect bubbleFrame = NSInsetRect(decorationFrame, 0.5, 0.5);
bubbleFrame.origin.x -= kLeftSideOverdraw;
bubbleFrame.size.width += kLeftSideOverdraw;
NSBezierPath* path =
[NSBezierPath bezierPathWithRoundedRect:bubbleFrame
xRadius:kBubbleCornerRadius
yRadius:kBubbleCornerRadius];
[background_color_ setFill];
[path fill];
[border_color_ setStroke];
[path setLineWidth:1.0];
[path stroke];
CGFloat textOffset = NSMinX(decorationFrame);
if (image_) {
// Center the image vertically.
const NSSize imageSize = [image_ size];
NSRect imageRect = decorationFrame;
imageRect.origin.y +=
std::floor((NSHeight(decorationFrame) - imageSize.height) / 2.0);
imageRect.size = imageSize;
[image_ drawInRect:imageRect
fromRect:NSZeroRect // Entire image
operation:NSCompositeSourceOver
fraction:1.0
respectFlipped:YES
hints:nil];
textOffset = NSMaxX(imageRect) + kIconLabelPadding;
}
if (label_) {
NSRect textRect = frame;
textRect.origin.x = textOffset;
textRect.size.width = NSMaxX(decorationFrame) - NSMinX(textRect);
DrawLabel(label_, attributes_, textRect);
}
}
NSImage* BubbleDecoration::GetImage() {
return image_;
}
void BubbleDecoration::SetImage(NSImage* image) {
image_.reset([image retain]);
}
void BubbleDecoration::SetLabel(NSString* label) {
// If the initializer was called with |nil|, then the code cannot
// process a label.
DCHECK(attributes_);
if (attributes_)
label_.reset([label copy]);
}
void BubbleDecoration::SetColors(NSColor* border_color,
NSColor* background_color,
NSColor* text_color) {
border_color_.reset([border_color retain]);
background_color_.reset([background_color retain]);
[attributes_ setObject:text_color forKey:NSForegroundColorAttributeName];
}