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