| 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 |