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/views/notifications/balloon_view.h" | 5 #include "chrome/browser/views/notifications/balloon_view.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "app/l10n_util.h" | 9 #include "app/l10n_util.h" |
10 #include "app/resource_bundle.h" | 10 #include "app/resource_bundle.h" |
11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
13 #include "chrome/browser/browser_theme_provider.h" | 13 #include "chrome/browser/browser_theme_provider.h" |
14 #include "chrome/browser/notifications/balloon.h" | 14 #include "chrome/browser/notifications/balloon.h" |
15 #include "chrome/browser/notifications/balloon_collection.h" | 15 #include "chrome/browser/notifications/balloon_collection.h" |
16 #include "chrome/browser/notifications/desktop_notification_service.h" | 16 #include "chrome/browser/notifications/desktop_notification_service.h" |
17 #include "chrome/browser/profile.h" | 17 #include "chrome/browser/profile.h" |
18 #include "chrome/browser/renderer_host/render_view_host.h" | 18 #include "chrome/browser/renderer_host/render_view_host.h" |
19 #include "chrome/browser/renderer_host/render_widget_host_view.h" | 19 #include "chrome/browser/renderer_host/render_widget_host_view.h" |
| 20 #include "chrome/browser/views/bubble_border.h" |
20 #include "chrome/browser/views/notifications/balloon_view_host.h" | 21 #include "chrome/browser/views/notifications/balloon_view_host.h" |
21 #include "chrome/common/notification_details.h" | 22 #include "chrome/common/notification_details.h" |
22 #include "chrome/common/notification_source.h" | 23 #include "chrome/common/notification_source.h" |
23 #include "chrome/common/notification_type.h" | 24 #include "chrome/common/notification_type.h" |
24 #include "gfx/canvas.h" | 25 #include "gfx/canvas.h" |
25 #include "gfx/insets.h" | 26 #include "gfx/insets.h" |
26 #include "gfx/native_widget_types.h" | 27 #include "gfx/native_widget_types.h" |
27 #include "grit/generated_resources.h" | 28 #include "grit/generated_resources.h" |
28 #include "grit/theme_resources.h" | 29 #include "grit/theme_resources.h" |
29 #include "views/controls/button/button.h" | 30 #include "views/controls/button/button.h" |
30 #include "views/controls/button/image_button.h" | 31 #include "views/controls/button/image_button.h" |
31 #include "views/controls/button/text_button.h" | 32 #include "views/controls/button/text_button.h" |
32 #include "views/controls/menu/menu_2.h" | 33 #include "views/controls/menu/menu_2.h" |
33 #include "views/controls/native/native_view_host.h" | 34 #include "views/controls/native/native_view_host.h" |
34 #include "views/painter.h" | 35 #include "views/painter.h" |
35 #include "views/widget/root_view.h" | 36 #include "views/widget/root_view.h" |
36 #if defined(OS_WIN) | 37 #if defined(OS_WIN) |
37 #include "views/widget/widget_win.h" | 38 #include "views/widget/widget_win.h" |
38 #endif | 39 #endif |
39 #if defined(OS_LINUX) | 40 #if defined(OS_LINUX) |
40 #include "views/widget/widget_gtk.h" | 41 #include "views/widget/widget_gtk.h" |
41 #endif | 42 #endif |
42 | 43 |
43 using views::Widget; | 44 using views::Widget; |
44 | 45 |
45 namespace { | 46 namespace { |
46 | 47 |
47 // How many pixels of overlap there is between the shelf top and the | 48 // How many pixels of overlap there is between the shelf top and the |
48 // balloon bottom. | 49 // balloon bottom. |
49 const int kTopMargin = 1; | 50 const int kTopMargin = 2; |
50 const int kBottomMargin = 0; | 51 const int kBottomMargin = 0; |
51 const int kLeftMargin = 1; | 52 const int kLeftMargin = 4; |
52 const int kRightMargin = 1; | 53 const int kRightMargin = 4; |
53 const int kShelfBorderTopOverlap = 0; | 54 const int kShelfBorderTopOverlap = 0; |
54 | 55 |
55 // Properties of the dismiss button. | 56 // Properties of the dismiss button. |
56 const int kDismissButtonWidth = 14; | 57 const int kDismissButtonWidth = 14; |
57 const int kDismissButtonHeight = 14; | 58 const int kDismissButtonHeight = 14; |
58 const int kDismissButtonTopMargin = 4; | 59 const int kDismissButtonTopMargin = 6; |
59 const int kDismissButtonRightMargin = 8; | 60 const int kDismissButtonRightMargin = 10; |
60 | 61 |
61 // Properties of the options menu. | 62 // Properties of the options menu. |
62 const int kOptionsMenuWidth = 55; | 63 const int kOptionsMenuWidth = 60; |
63 const int kOptionsMenuHeight = 20; | 64 const int kOptionsMenuHeight = 20; |
64 | 65 |
65 // Properties of the origin label. | 66 // Properties of the origin label. |
66 const int kLeftLabelMargin = 5; | 67 const int kLeftLabelMargin = 10; |
67 | 68 |
68 // Size of the drop shadow. | 69 // Size of the drop shadow. |
69 const int kLeftShadowWidth = 5; | 70 const int kLeftShadowWidth = 0; |
70 const int kRightShadowWidth = 5; | 71 const int kRightShadowWidth = 0; |
71 const int kTopShadowWidth = 0; | 72 const int kTopShadowWidth = 0; |
72 const int kBottomShadowWidth = 5; | 73 const int kBottomShadowWidth = 6; |
73 | 74 |
74 // Optional animation. | 75 // Optional animation. |
75 const bool kAnimateEnabled = true; | 76 const bool kAnimateEnabled = true; |
76 | 77 |
77 // The shelf height for the system default font size. It is scaled | 78 // The shelf height for the system default font size. It is scaled |
78 // with changes in the default font size. | 79 // with changes in the default font size. |
79 const int kDefaultShelfHeight = 22; | 80 const int kDefaultShelfHeight = 22; |
80 | 81 |
81 // Menu commands | 82 // Menu commands |
82 const int kRevokePermissionCommand = 0; | 83 const int kRevokePermissionCommand = 0; |
83 | 84 |
84 } // namespace | 85 } // namespace |
85 | 86 |
86 BalloonViewImpl::BalloonViewImpl(BalloonCollection* collection) | 87 BalloonViewImpl::BalloonViewImpl(BalloonCollection* collection) |
87 : balloon_(NULL), | 88 : balloon_(NULL), |
88 collection_(collection), | 89 collection_(collection), |
89 frame_container_(NULL), | 90 frame_container_(NULL), |
90 html_container_(NULL), | 91 html_container_(NULL), |
91 html_contents_(NULL), | 92 html_contents_(NULL), |
92 method_factory_(this), | 93 method_factory_(this), |
93 shelf_background_(NULL), | |
94 balloon_background_(NULL), | |
95 close_button_(NULL), | 94 close_button_(NULL), |
96 animation_(NULL), | 95 animation_(NULL), |
97 options_menu_contents_(NULL), | 96 options_menu_contents_(NULL), |
98 options_menu_menu_(NULL), | 97 options_menu_menu_(NULL), |
99 options_menu_button_(NULL) { | 98 options_menu_button_(NULL) { |
100 // This object is not to be deleted by the views hierarchy, | 99 // This object is not to be deleted by the views hierarchy, |
101 // as it is owned by the balloon. | 100 // as it is owned by the balloon. |
102 set_parent_owned(false); | 101 set_parent_owned(false); |
103 | 102 |
104 // Load the sprites for the frames. | 103 BubbleBorder* bubble_border = new BubbleBorder(BubbleBorder::FLOAT); |
105 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 104 set_border(bubble_border); |
106 SkBitmap* shelf_bitmap = rb.GetBitmapNamed(IDR_BALLOON_SHELF); | |
107 SkBitmap* border_bitmap = rb.GetBitmapNamed(IDR_BALLOON_BORDER); | |
108 | |
109 gfx::Insets shelf_insets(1, 9, 9, 9); | |
110 shelf_background_.reset( | |
111 views::Painter::CreateImagePainter(*shelf_bitmap, shelf_insets, true)); | |
112 gfx::Insets border_insets(4, 9, 1, 9); | |
113 balloon_background_.reset( | |
114 views::Painter::CreateImagePainter(*border_bitmap, border_insets, false)); | |
115 } | 105 } |
116 | 106 |
117 BalloonViewImpl::~BalloonViewImpl() { | 107 BalloonViewImpl::~BalloonViewImpl() { |
118 } | 108 } |
119 | 109 |
120 void BalloonViewImpl::Close(bool by_user) { | 110 void BalloonViewImpl::Close(bool by_user) { |
121 MessageLoop::current()->PostTask(FROM_HERE, | 111 MessageLoop::current()->PostTask(FROM_HERE, |
122 method_factory_.NewRunnableMethod( | 112 method_factory_.NewRunnableMethod( |
123 &BalloonViewImpl::DelayedClose, by_user)); | 113 &BalloonViewImpl::DelayedClose, by_user)); |
124 } | 114 } |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 | 314 |
325 close_button_->SetImage(views::CustomButton::BS_NORMAL, | 315 close_button_->SetImage(views::CustomButton::BS_NORMAL, |
326 rb.GetBitmapNamed(IDR_BALLOON_CLOSE)); | 316 rb.GetBitmapNamed(IDR_BALLOON_CLOSE)); |
327 close_button_->SetImage(views::CustomButton::BS_HOT, | 317 close_button_->SetImage(views::CustomButton::BS_HOT, |
328 rb.GetBitmapNamed(IDR_BALLOON_CLOSE_HOVER)); | 318 rb.GetBitmapNamed(IDR_BALLOON_CLOSE_HOVER)); |
329 close_button_->SetImage(views::CustomButton::BS_PUSHED, | 319 close_button_->SetImage(views::CustomButton::BS_PUSHED, |
330 rb.GetBitmapNamed(IDR_BALLOON_CLOSE_HOVER)); | 320 rb.GetBitmapNamed(IDR_BALLOON_CLOSE_HOVER)); |
331 close_button_->SetBounds(GetCloseButtonBounds()); | 321 close_button_->SetBounds(GetCloseButtonBounds()); |
332 | 322 |
333 options_menu_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont)); | 323 options_menu_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont)); |
334 options_menu_button_->SetIcon(*rb.GetBitmapNamed(IDR_BALLOON_OPTIONS_ARROW_HOV
ER)); | 324 options_menu_button_->SetIcon( |
| 325 *rb.GetBitmapNamed(IDR_BALLOON_OPTIONS_ARROW_HOVER)); |
335 options_menu_button_->SetHoverIcon( | 326 options_menu_button_->SetHoverIcon( |
336 *rb.GetBitmapNamed(IDR_BALLOON_OPTIONS_ARROW_HOVER)); | 327 *rb.GetBitmapNamed(IDR_BALLOON_OPTIONS_ARROW_HOVER)); |
337 options_menu_button_->set_alignment(views::TextButton::ALIGN_CENTER); | 328 options_menu_button_->set_alignment(views::TextButton::ALIGN_CENTER); |
338 options_menu_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT); | 329 options_menu_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT); |
339 options_menu_button_->SetEnabledColor(SK_ColorDKGRAY); | 330 options_menu_button_->SetEnabledColor(SK_ColorDKGRAY); |
340 options_menu_button_->SetHoverColor(SK_ColorDKGRAY); | 331 options_menu_button_->SetHoverColor(SK_ColorDKGRAY); |
341 options_menu_button_->SetBounds(GetOptionsMenuBounds()); | 332 options_menu_button_->SetBounds(GetOptionsMenuBounds()); |
342 | 333 |
343 source_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont)); | 334 source_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont)); |
344 source_label_->SetColor(SK_ColorDKGRAY); | 335 source_label_->SetColor(SK_ColorDKGRAY); |
(...skipping 22 matching lines...) Expand all Loading... |
367 this->balloon_->notification().display_source())); | 358 this->balloon_->notification().display_source())); |
368 | 359 |
369 options_menu_contents_.reset(new menus::SimpleMenuModel(this)); | 360 options_menu_contents_.reset(new menus::SimpleMenuModel(this)); |
370 options_menu_contents_->AddItem(kRevokePermissionCommand, label_text); | 361 options_menu_contents_->AddItem(kRevokePermissionCommand, label_text); |
371 | 362 |
372 options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get())); | 363 options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get())); |
373 } | 364 } |
374 | 365 |
375 void BalloonViewImpl::GetContentsMask(const gfx::Rect& rect, | 366 void BalloonViewImpl::GetContentsMask(const gfx::Rect& rect, |
376 gfx::Path* path) const { | 367 gfx::Path* path) const { |
| 368 // This rounds the corners, and we also cut out a circle for the close |
| 369 // button, since we can't guarantee the ordering of two top-most windows. |
| 370 SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius()); |
| 371 SkScalar scaled_radius = |
| 372 SkScalarMul(radius, (SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3); |
| 373 SkScalar width = SkIntToScalar(rect.width()); |
| 374 SkScalar height = SkIntToScalar(rect.height()); |
377 | 375 |
378 gfx::Point cutout = GetCloseButtonBounds().CenterPoint(); | 376 gfx::Point cutout = GetCloseButtonBounds().CenterPoint(); |
379 cutout = cutout.Subtract(GetContentsOffset()); | 377 cutout = cutout.Subtract(GetContentsOffset()); |
380 // This needs to remove areas that look like the following from each corner: | 378 SkScalar cutout_x = SkIntToScalar(cutout.x()) - SkScalar(0.5); |
381 // | 379 SkScalar cutout_y = SkIntToScalar(cutout.y()) - SkScalar(0.5); |
382 // xx | 380 SkScalar cutout_radius = SkIntToScalar(kDismissButtonWidth) / SkScalar(2.0); |
383 // x | 381 |
384 // | 382 path->moveTo(radius, 0); |
385 // We also cut out a circle for the close button, since we can't guarantee | 383 path->lineTo(cutout_x, 0); |
386 // the ordering of two TOP_MOST windows. | 384 path->addCircle(cutout_x, cutout_y, cutout_radius); |
387 path->moveTo(SkScalar(0.5), SkScalar(0)); | 385 path->lineTo(cutout_x, 0); |
388 path->lineTo(SkScalar(cutout.x() - 0.5), SkScalar(0)); | 386 path->lineTo(width - radius, 0); |
389 path->addCircle(SkScalar(cutout.x() - 0.5), SkScalar(cutout.y() - 0.5), | 387 path->cubicTo(width - radius + scaled_radius, 0, |
390 SkScalar(kDismissButtonWidth / 2.0)); | 388 width, radius - scaled_radius, |
391 path->lineTo(SkScalar(cutout.x() - 0.5), SkScalar(0)); | 389 width, radius); |
392 // Upper right corner | 390 path->lineTo(width, height); |
393 path->arcTo(rect.width() - SkScalar(1.5), SkScalar(0), | 391 path->lineTo(0, height); |
394 rect.width() - SkScalar(0.5), SkScalar(1.5), | 392 path->lineTo(0, radius); |
395 SkScalar(1)); | 393 path->cubicTo(0, radius - scaled_radius, |
396 // Lower right corner | 394 radius - scaled_radius, 0, |
397 path->lineTo(rect.width() - SkScalar(0.5), SkScalar(rect.height())); | 395 radius, 0); |
398 // Lower left corner | 396 path->close(); |
399 path->lineTo(SkScalar(0), SkScalar(rect.height())); | 397 } |
400 // Upper left corner | 398 |
401 path->arcTo(0, SkScalar(0.5), SkScalar(0.5), 0, SkScalar(1)); | 399 void BalloonViewImpl::GetFrameMask(const gfx::Rect& bounding_rect, |
| 400 gfx::Path* path) const { |
| 401 SkRect rect; |
| 402 rect.set(SkIntToScalar(bounding_rect.x()), |
| 403 SkIntToScalar(bounding_rect.y()), |
| 404 SkIntToScalar(bounding_rect.right()), |
| 405 SkIntToScalar(bounding_rect.bottom())); |
| 406 |
| 407 SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius()); |
| 408 SkScalar scaled_radius = |
| 409 SkScalarMul(radius, (SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3); |
| 410 path->moveTo(rect.fRight, rect.fTop); |
| 411 path->lineTo(rect.fRight, rect.fBottom - radius); |
| 412 path->cubicTo(rect.fRight, rect.fBottom - radius + scaled_radius, |
| 413 rect.fRight - radius + scaled_radius, rect.fBottom, |
| 414 rect.fRight - radius, rect.fBottom); |
| 415 path->lineTo(rect.fLeft + radius, rect.fBottom); |
| 416 path->cubicTo(rect.fLeft + radius - scaled_radius, rect.fBottom, |
| 417 rect.fLeft, rect.fBottom - radius + scaled_radius, |
| 418 rect.fLeft, rect.fBottom - radius); |
| 419 path->lineTo(rect.fLeft, rect.fTop); |
| 420 path->close(); |
402 } | 421 } |
403 | 422 |
404 gfx::Point BalloonViewImpl::GetContentsOffset() const { | 423 gfx::Point BalloonViewImpl::GetContentsOffset() const { |
405 return gfx::Point(kLeftShadowWidth + kLeftMargin, | 424 return gfx::Point(kLeftShadowWidth + kLeftMargin, |
406 kTopShadowWidth + kTopMargin); | 425 kTopShadowWidth + kTopMargin); |
407 } | 426 } |
408 | 427 |
409 int BalloonViewImpl::GetShelfHeight() const { | 428 int BalloonViewImpl::GetShelfHeight() const { |
410 // TODO(johnnyg): add scaling here. | 429 // TODO(johnnyg): add scaling here. |
411 return kDefaultShelfHeight + kBottomShadowWidth; | 430 return kDefaultShelfHeight + kBottomShadowWidth; |
(...skipping 20 matching lines...) Expand all Loading... |
432 gfx::Size content_size = balloon_->content_size(); | 451 gfx::Size content_size = balloon_->content_size(); |
433 gfx::Point offset = GetContentsOffset(); | 452 gfx::Point offset = GetContentsOffset(); |
434 gfx::Rect frame_rect; | 453 gfx::Rect frame_rect; |
435 frame_container_->GetBounds(&frame_rect, true); | 454 frame_container_->GetBounds(&frame_rect, true); |
436 return gfx::Rect(frame_rect.x() + offset.x(), frame_rect.y() + offset.y(), | 455 return gfx::Rect(frame_rect.x() + offset.x(), frame_rect.y() + offset.y(), |
437 content_size.width(), content_size.height()); | 456 content_size.width(), content_size.height()); |
438 } | 457 } |
439 | 458 |
440 void BalloonViewImpl::Paint(gfx::Canvas* canvas) { | 459 void BalloonViewImpl::Paint(gfx::Canvas* canvas) { |
441 DCHECK(canvas); | 460 DCHECK(canvas); |
442 int background_width = GetTotalWidth(); | 461 // Paint the menu bar area white, with proper rounded corners. |
443 int background_height = GetBalloonFrameHeight(); | 462 gfx::Path path; |
444 balloon_background_->Paint(background_width, background_height, canvas); | 463 gfx::Rect rect = GetLocalBounds(false); |
445 canvas->save(); | 464 rect.set_y(GetBalloonFrameHeight()); |
446 SkScalar y_offset = | 465 rect.set_height(GetShelfHeight() - kBottomShadowWidth); |
447 static_cast<SkScalar>(background_height - kShelfBorderTopOverlap); | 466 GetFrameMask(rect, &path); |
448 canvas->translate(0, y_offset); | 467 |
449 shelf_background_->Paint(background_width, GetShelfHeight(), canvas); | 468 SkPaint paint; |
450 canvas->restore(); | 469 paint.setAntiAlias(true); |
| 470 paint.setColor(SK_ColorWHITE); |
| 471 canvas->drawPath(path, paint); |
| 472 |
| 473 // Draw a 1-pixel gray line between the content and the menu bar. |
| 474 int line_width = GetTotalWidth() - kLeftMargin - kRightMargin; |
| 475 canvas->FillRectInt( |
| 476 SK_ColorLTGRAY, kLeftMargin, GetBalloonFrameHeight(), line_width, 1); |
451 | 477 |
452 View::Paint(canvas); | 478 View::Paint(canvas); |
| 479 PaintBorder(canvas); |
453 } | 480 } |
454 | 481 |
455 // menus::SimpleMenuModel::Delegate methods | 482 // menus::SimpleMenuModel::Delegate methods |
456 bool BalloonViewImpl::IsCommandIdChecked(int /* command_id */) const { | 483 bool BalloonViewImpl::IsCommandIdChecked(int /* command_id */) const { |
457 // Nothing in the menu is checked. | 484 // Nothing in the menu is checked. |
458 return false; | 485 return false; |
459 } | 486 } |
460 | 487 |
461 bool BalloonViewImpl::IsCommandIdEnabled(int /* command_id */) const { | 488 bool BalloonViewImpl::IsCommandIdEnabled(int /* command_id */) const { |
462 // All the menu options are always enabled. | 489 // All the menu options are always enabled. |
(...skipping 25 matching lines...) Expand all Loading... |
488 NOTREACHED(); | 515 NOTREACHED(); |
489 return; | 516 return; |
490 } | 517 } |
491 | 518 |
492 // If the renderer process attached to this balloon is disconnected | 519 // If the renderer process attached to this balloon is disconnected |
493 // (e.g., because of a crash), we want to close the balloon. | 520 // (e.g., because of a crash), we want to close the balloon. |
494 notification_registrar_.Remove(this, | 521 notification_registrar_.Remove(this, |
495 NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_)); | 522 NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_)); |
496 Close(false); | 523 Close(false); |
497 } | 524 } |
OLD | NEW |