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