OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "chrome/browser/cocoa/status_bubble_mac.h" | 5 #include "chrome/browser/cocoa/status_bubble_mac.h" |
6 | 6 |
7 #include "app/gfx/text_elider.h" | 7 #include "app/gfx/text_elider.h" |
8 #include "base/string_util.h" | 8 #include "base/string_util.h" |
9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
| 10 #import "chrome/browser/cocoa/bubble_view.h" |
10 #include "googleurl/src/gurl.h" | 11 #include "googleurl/src/gurl.h" |
11 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" | 12 #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" |
12 #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" | 13 #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" |
13 #import "third_party/GTM/AppKit/GTMTheme.h" | 14 #import "third_party/GTM/AppKit/GTMTheme.h" |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
17 const int kWindowHeight = 18; | 18 const int kWindowHeight = 18; |
18 // The width of the bubble in relation to the width of the parent window. | 19 // The width of the bubble in relation to the width of the parent window. |
19 const float kWindowWidthPercent = 1.0f/3.0f; | 20 const float kWindowWidthPercent = 1.0f/3.0f; |
20 | 21 |
21 // How close the mouse can get to the infobubble before it starts sliding | 22 // How close the mouse can get to the infobubble before it starts sliding |
22 // off-screen. | 23 // off-screen. |
23 const int kMousePadding = 20; | 24 const int kMousePadding = 20; |
24 | 25 |
25 const int kTextPadding = 3; | 26 const int kTextPadding = 3; |
26 const int kTextPositionX = 4; | |
27 const int kTextPositionY = 2; | |
28 | |
29 const float kWindowFill = 0.8f; | |
30 const float kWindowEdge = 0.7f; | |
31 | |
32 // The roundedness of the edges of our bubble. | |
33 const int kBubbleCornerRadius = 4.0f; | |
34 | 27 |
35 // How long each fade should last for. | 28 // How long each fade should last for. |
36 const int kShowFadeDuration = 0.120f; | 29 const int kShowFadeDuration = 0.120f; |
37 const int kHideFadeDuration = 0.200f; | 30 const int kHideFadeDuration = 0.200f; |
38 | 31 |
39 } | 32 } |
40 | 33 |
41 // TODO(avi): | 34 // TODO(avi): |
42 // - do display delay | 35 // - do display delay |
43 | 36 |
44 enum BubbleStyle { | |
45 STYLE_BOTTOM, // Hanging off the bottom of the parent window | |
46 STYLE_FLOATING, // Between BOTTOM and STANDARD | |
47 STYLE_STANDARD // Nestled in the corner of the parent window | |
48 }; | |
49 | |
50 @interface StatusBubbleViewCocoa : NSView { | |
51 @private | |
52 NSString* content_; | |
53 BubbleStyle style_; | |
54 NSWindow* parent_; | |
55 } | |
56 | |
57 - (void)setContent:(NSString*)content; | |
58 - (void)setStyle:(BubbleStyle)style; | |
59 - (void)setParent:(NSWindow*)parent; | |
60 - (NSFont*)font; | |
61 @end | |
62 | |
63 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) | 37 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) |
64 : parent_(parent), | 38 : parent_(parent), |
65 delegate_(delegate), | 39 delegate_(delegate), |
66 window_(nil), | 40 window_(nil), |
67 status_text_(nil), | 41 status_text_(nil), |
68 url_text_(nil), | 42 url_text_(nil), |
69 is_download_shelf_visible_(false) { | 43 is_download_shelf_visible_(false) { |
70 } | 44 } |
71 | 45 |
72 StatusBubbleMac::~StatusBubbleMac() { | 46 StatusBubbleMac::~StatusBubbleMac() { |
73 Hide(); | 47 Hide(); |
74 } | 48 } |
75 | 49 |
76 void StatusBubbleMac::SetStatus(const std::wstring& status) { | 50 void StatusBubbleMac::SetStatus(const std::wstring& status) { |
77 Create(); | 51 Create(); |
78 | 52 |
79 NSString* status_ns = base::SysWideToNSString(status); | 53 NSString* status_ns = base::SysWideToNSString(status); |
80 | 54 |
81 SetStatus(status_ns, false); | 55 SetStatus(status_ns, false); |
82 } | 56 } |
83 | 57 |
84 void StatusBubbleMac::SetURL(const GURL& url, const std::wstring& languages) { | 58 void StatusBubbleMac::SetURL(const GURL& url, const std::wstring& languages) { |
85 Create(); | 59 Create(); |
86 | 60 |
87 NSRect frame = [window_ frame]; | 61 NSRect frame = [window_ frame]; |
88 int text_width = static_cast<int>(frame.size.width - | 62 int text_width = static_cast<int>(frame.size.width - |
89 kTextPositionX - | 63 kBubbleViewTextPositionX - |
90 kTextPadding); | 64 kTextPadding); |
91 NSFont* font = [[window_ contentView] font]; | 65 NSFont* font = [[window_ contentView] font]; |
92 gfx::Font font_chr = | 66 gfx::Font font_chr = |
93 gfx::Font::CreateFont(base::SysNSStringToWide([font fontName]), | 67 gfx::Font::CreateFont(base::SysNSStringToWide([font fontName]), |
94 [font pointSize]); | 68 [font pointSize]); |
95 | 69 |
96 std::wstring status = gfx::ElideUrl(url, font_chr, text_width, languages); | 70 std::wstring status = gfx::ElideUrl(url, font_chr, text_width, languages); |
97 NSString* status_ns = base::SysWideToNSString(status); | 71 NSString* status_ns = base::SysWideToNSString(status); |
98 | 72 |
99 SetStatus(status_ns, true); | 73 SetStatus(status_ns, true); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 // scaled by how horizontally far away the cursor is from the bubble. | 144 // scaled by how horizontally far away the cursor is from the bubble. |
171 if (cursor_location.x > 0) { | 145 if (cursor_location.x > 0) { |
172 offset = offset * ((kMousePadding - cursor_location.x) / kMousePadding); | 146 offset = offset * ((kMousePadding - cursor_location.x) / kMousePadding); |
173 } | 147 } |
174 | 148 |
175 // Cap the offset and change the visual presentation of the bubble | 149 // Cap the offset and change the visual presentation of the bubble |
176 // depending on where it ends up (so that rounded corners square off | 150 // depending on where it ends up (so that rounded corners square off |
177 // and mate to the edges of the tab content). | 151 // and mate to the edges of the tab content). |
178 if (offset >= NSHeight(window_frame)) { | 152 if (offset >= NSHeight(window_frame)) { |
179 offset = NSHeight(window_frame); | 153 offset = NSHeight(window_frame); |
180 [[window_ contentView] setStyle:STYLE_BOTTOM]; | 154 [[window_ contentView] setCornerFlags: |
| 155 kRoundedBottomLeftCorner | kRoundedBottomRightCorner]; |
181 } else if (offset > 0) { | 156 } else if (offset > 0) { |
182 [[window_ contentView] setStyle:STYLE_FLOATING]; | 157 [[window_ contentView] setCornerFlags: |
| 158 kRoundedTopRightCorner | kRoundedBottomLeftCorner | |
| 159 kRoundedBottomRightCorner]; |
183 } else { | 160 } else { |
184 [[window_ contentView] setStyle:STYLE_STANDARD]; | 161 [[window_ contentView] setCornerFlags:kRoundedTopRightCorner]; |
185 } | 162 } |
186 | 163 |
187 offset_ = offset; | 164 offset_ = offset; |
188 window_frame.origin.y -= offset; | 165 window_frame.origin.y -= offset; |
189 } else { | 166 } else { |
190 offset_ = 0; | 167 offset_ = 0; |
191 [[window_ contentView] setStyle:STYLE_STANDARD]; | 168 [[window_ contentView] setCornerFlags:kRoundedTopRightCorner]; |
192 } | 169 } |
193 | 170 |
194 // |delegate_| can be nil during unit tests. | 171 // |delegate_| can be nil during unit tests. |
195 if (is_download_shelf_visible_) { | 172 if (is_download_shelf_visible_) { |
196 if ([delegate_ respondsToSelector:@selector(verticalOffsetForStatusBubble)]) | 173 if ([delegate_ respondsToSelector:@selector(verticalOffsetForStatusBubble)]) |
197 window_frame.origin.y += [delegate_ verticalOffsetForStatusBubble]; | 174 window_frame.origin.y += [delegate_ verticalOffsetForStatusBubble]; |
198 } | 175 } |
199 | 176 |
200 [window_ setFrame:window_frame display:YES]; | 177 [window_ setFrame:window_frame display:YES]; |
201 } | 178 } |
(...skipping 13 matching lines...) Expand all Loading... |
215 window_ = [[NSWindow alloc] initWithContentRect:rect | 192 window_ = [[NSWindow alloc] initWithContentRect:rect |
216 styleMask:NSBorderlessWindowMask | 193 styleMask:NSBorderlessWindowMask |
217 backing:NSBackingStoreBuffered | 194 backing:NSBackingStoreBuffered |
218 defer:YES]; | 195 defer:YES]; |
219 [window_ setMovableByWindowBackground:NO]; | 196 [window_ setMovableByWindowBackground:NO]; |
220 [window_ setBackgroundColor:[NSColor clearColor]]; | 197 [window_ setBackgroundColor:[NSColor clearColor]]; |
221 [window_ setLevel:NSNormalWindowLevel]; | 198 [window_ setLevel:NSNormalWindowLevel]; |
222 [window_ setOpaque:NO]; | 199 [window_ setOpaque:NO]; |
223 [window_ setHasShadow:NO]; | 200 [window_ setHasShadow:NO]; |
224 | 201 |
225 StatusBubbleViewCocoa* view = | 202 // We do not need to worry about the bubble outliving |parent_| because our |
226 [[[StatusBubbleViewCocoa alloc] initWithFrame:NSZeroRect] autorelease]; | 203 // teardown sequence in BWC guarantees that |parent_| outlives the status |
227 [view setParent:parent_]; | 204 // bubble and that the StatusBubble is torn down completely prior to the |
228 | 205 // window going away. |
| 206 scoped_nsobject<BubbleView> view( |
| 207 [[BubbleView alloc] initWithFrame:NSZeroRect themeProvider:parent_]); |
229 [window_ setContentView:view]; | 208 [window_ setContentView:view]; |
230 | 209 |
231 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; | 210 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; |
232 | 211 |
233 [window_ setAlphaValue:0.0f]; | 212 [window_ setAlphaValue:0.0f]; |
234 | 213 |
235 offset_ = 0; | 214 offset_ = 0; |
236 [view setStyle:STYLE_STANDARD]; | 215 [view setCornerFlags:kRoundedTopRightCorner]; |
237 MouseMoved(); | 216 MouseMoved(); |
238 } | 217 } |
239 | 218 |
240 void StatusBubbleMac::FadeIn() { | 219 void StatusBubbleMac::FadeIn() { |
241 [NSAnimationContext beginGrouping]; | 220 [NSAnimationContext beginGrouping]; |
242 [[NSAnimationContext currentContext] setDuration:kShowFadeDuration]; | 221 [[NSAnimationContext currentContext] setDuration:kShowFadeDuration]; |
243 [[window_ animator] setAlphaValue:1.0f]; | 222 [[window_ animator] setAlphaValue:1.0f]; |
244 [NSAnimationContext endGrouping]; | 223 [NSAnimationContext endGrouping]; |
245 } | 224 } |
246 | 225 |
247 void StatusBubbleMac::FadeOut() { | 226 void StatusBubbleMac::FadeOut() { |
248 [NSAnimationContext beginGrouping]; | 227 [NSAnimationContext beginGrouping]; |
249 [[NSAnimationContext currentContext] setDuration:kHideFadeDuration]; | 228 [[NSAnimationContext currentContext] setDuration:kHideFadeDuration]; |
250 [[window_ animator] setAlphaValue:0.0f]; | 229 [[window_ animator] setAlphaValue:0.0f]; |
251 [NSAnimationContext endGrouping]; | 230 [NSAnimationContext endGrouping]; |
252 } | 231 } |
253 | 232 |
254 @implementation StatusBubbleViewCocoa | |
255 | |
256 - (void)dealloc { | |
257 [parent_ release]; | |
258 [content_ release]; | |
259 [super dealloc]; | |
260 } | |
261 | |
262 - (void)setContent:(NSString*)content { | |
263 [content_ autorelease]; | |
264 content_ = [content copy]; | |
265 [self setNeedsDisplay:YES]; | |
266 } | |
267 | |
268 - (void)setStyle:(BubbleStyle)style { | |
269 style_ = style; | |
270 [self setNeedsDisplay:YES]; | |
271 } | |
272 | |
273 - (void)setParent:(NSWindow*)parent { | |
274 [parent_ autorelease]; | |
275 parent_ = [parent retain]; | |
276 [self setNeedsDisplay:YES]; | |
277 } | |
278 | |
279 - (GTMTheme*)gtm_theme { | |
280 return [parent_ gtm_theme]; | |
281 } | |
282 | |
283 - (NSFont*)font { | |
284 return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; | |
285 } | |
286 | |
287 - (void)drawRect:(NSRect)rect { | |
288 float tl_radius, tr_radius, bl_radius, br_radius; | |
289 | |
290 switch (style_) { | |
291 case STYLE_BOTTOM: | |
292 tl_radius = 0.0f; | |
293 tr_radius = 0.0f; | |
294 bl_radius = kBubbleCornerRadius; | |
295 br_radius = kBubbleCornerRadius; | |
296 break; | |
297 case STYLE_FLOATING: | |
298 tl_radius = 0.0f; | |
299 tr_radius = kBubbleCornerRadius; | |
300 bl_radius = kBubbleCornerRadius; | |
301 br_radius = kBubbleCornerRadius; | |
302 break; | |
303 case STYLE_STANDARD: | |
304 tl_radius = 0.0f; | |
305 tr_radius = kBubbleCornerRadius; | |
306 bl_radius = 0.0f; | |
307 br_radius = 0.0f; | |
308 break; | |
309 default: | |
310 NOTREACHED(); | |
311 tl_radius = 0.0f; | |
312 tr_radius = 0.0f; | |
313 bl_radius = 0.0f; | |
314 br_radius = 0.0f; | |
315 } | |
316 | |
317 // Background / Edge | |
318 | |
319 NSRect bounds = [self bounds]; | |
320 bounds = NSInsetRect(bounds, 0.5, 0.5); | |
321 NSBezierPath *border = [NSBezierPath gtm_bezierPathWithRoundRect:bounds | |
322 topLeftCornerRadius:tl_radius | |
323 topRightCornerRadius:tr_radius | |
324 bottomLeftCornerRadius:bl_radius | |
325 bottomRightCornerRadius:br_radius]; | |
326 | |
327 NSColor* color = | |
328 [[self gtm_theme] backgroundColorForStyle:GTMThemeStyleToolBar | |
329 state:GTMThemeStateActiveWindow]; | |
330 | |
331 // workaround for default theme | |
332 // TODO(alcor) next GTM update return nil for background color if not set; | |
333 if ([color isEqual:[NSColor colorWithCalibratedWhite:0.5 alpha:1.0]]) | |
334 color = nil; | |
335 if (!color) | |
336 color = [NSColor colorWithCalibratedWhite:0.9 alpha:1.0]; | |
337 [color set]; | |
338 [border fill]; | |
339 | |
340 border = [NSBezierPath gtm_bezierPathWithRoundRect:bounds | |
341 topLeftCornerRadius:tl_radius | |
342 topRightCornerRadius:tr_radius | |
343 bottomLeftCornerRadius:bl_radius | |
344 bottomRightCornerRadius:br_radius]; | |
345 | |
346 [[NSColor colorWithDeviceWhite:kWindowEdge alpha:1.0f] set]; | |
347 [border stroke]; | |
348 | |
349 // Text | |
350 NSColor* textColor = [color gtm_legibleTextColor]; | |
351 NSFont* textFont = [self font]; | |
352 NSShadow* textShadow = [[[NSShadow alloc] init] autorelease]; | |
353 [textShadow setShadowBlurRadius:0.0f]; | |
354 [textShadow setShadowColor:[textColor gtm_legibleTextColor]]; | |
355 [textShadow setShadowOffset:NSMakeSize(0.0f, -1.0f)]; | |
356 | |
357 NSDictionary* textDict = [NSDictionary dictionaryWithObjectsAndKeys: | |
358 textColor, NSForegroundColorAttributeName, | |
359 textFont, NSFontAttributeName, | |
360 textShadow, NSShadowAttributeName, | |
361 nil]; | |
362 [content_ drawAtPoint:NSMakePoint(kTextPositionX, kTextPositionY) | |
363 withAttributes:textDict]; | |
364 } | |
365 | |
366 @end | |
OLD | NEW |