This source file includes following definitions.
- GetHorizontalMargin
- closed_
- Close
- GetSize
- GetHost
- OnMenuButtonClicked
- OnDisplayChanged
- OnWorkAreaChanged
- DeleteDelegate
- ButtonPressed
- GetPreferredSize
- SizeContentsWindow
- RepositionToBalloon
- Update
- AnimationProgressed
- GetCloseButtonBounds
- GetOptionsButtonBounds
- GetLabelBounds
- Show
- CreateOptionsMenu
- GetContentsMask
- GetFrameMask
- GetContentsOffset
- GetBoundsForFrameContainer
- GetShelfHeight
- GetBalloonFrameHeight
- GetTotalWidth
- GetTotalHeight
- GetContentsRectangle
- OnPaint
- OnBoundsChanged
- Observe
#include "chrome/browser/ui/views/notifications/balloon_view_views.h"
#include <algorithm>
#include <vector>
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/notifications/balloon_collection.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_options_menu_model.h"
#include "chrome/browser/ui/views/notifications/balloon_view_host.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/path.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/button/text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/widget/widget.h"
namespace {
const int kTopMargin = 2;
const int kBottomMargin = 0;
const int kLeftMargin = 4;
const int kRightMargin = 4;
const int kShelfMargin = 2;
const int kOptionsDismissSpacing = 4;
const int kLabelOptionsSpacing = 4;
const int kLabelLeftMargin = 6;
const int kLeftShadowWidth = 0;
const int kRightShadowWidth = 0;
const int kTopShadowWidth = 0;
const int kBottomShadowWidth = 6;
const bool kAnimateEnabled = true;
const SkColor kControlBarBackgroundColor = SkColorSetRGB(245, 245, 245);
const SkColor kControlBarTextColor = SkColorSetRGB(125, 125, 125);
const SkColor kControlBarSeparatorLineColor = SkColorSetRGB(180, 180, 180);
}
int BalloonView::GetHorizontalMargin() {
return kLeftMargin + kRightMargin + kLeftShadowWidth + kRightShadowWidth;
}
BalloonViewImpl::BalloonViewImpl(BalloonCollection* collection)
: balloon_(NULL),
collection_(collection),
frame_container_(NULL),
html_container_(NULL),
close_button_(NULL),
options_menu_button_(NULL),
enable_web_ui_(false),
closed_by_user_(false),
closed_(false) {
set_owned_by_client();
SetBorder(scoped_ptr<views::Border>(
new views::BubbleBorder(views::BubbleBorder::FLOAT,
views::BubbleBorder::NO_SHADOW,
SK_ColorWHITE)));
}
BalloonViewImpl::~BalloonViewImpl() {
}
void BalloonViewImpl::Close(bool by_user) {
if (closed_)
return;
closed_ = true;
animation_->Stop();
html_contents_->Shutdown();
html_container_->GetRootView()->RemoveAllChildViews(true);
html_container_->Close();
frame_container_->GetRootView()->RemoveAllChildViews(true);
frame_container_->Close();
closed_by_user_ = by_user;
}
gfx::Size BalloonViewImpl::GetSize() const {
if (!balloon_)
return gfx::Size(0, 0);
return gfx::Size(GetTotalWidth(), GetTotalHeight());
}
BalloonHost* BalloonViewImpl::GetHost() const {
return html_contents_.get();
}
void BalloonViewImpl::OnMenuButtonClicked(views::View* source,
const gfx::Point& point) {
CreateOptionsMenu();
menu_runner_.reset(new views::MenuRunner(options_menu_model_.get()));
gfx::Point screen_location;
views::View::ConvertPointToScreen(options_menu_button_, &screen_location);
if (menu_runner_->RunMenuAt(
source->GetWidget()->GetTopLevelWidget(),
options_menu_button_,
gfx::Rect(screen_location, options_menu_button_->size()),
views::MenuItemView::TOPRIGHT,
ui::MENU_SOURCE_NONE,
views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
return;
}
void BalloonViewImpl::OnDisplayChanged() {
collection_->DisplayChanged();
}
void BalloonViewImpl::OnWorkAreaChanged() {
collection_->DisplayChanged();
}
void BalloonViewImpl::DeleteDelegate() {
balloon_->OnClose(closed_by_user_);
}
void BalloonViewImpl::ButtonPressed(views::Button* sender, const ui::Event&) {
DCHECK_EQ(close_button_, sender);
Close(true);
}
gfx::Size BalloonViewImpl::GetPreferredSize() {
return gfx::Size(1000, 1000);
}
void BalloonViewImpl::SizeContentsWindow() {
if (!html_container_ || !frame_container_)
return;
gfx::Rect contents_rect = GetContentsRectangle();
html_container_->SetBounds(contents_rect);
html_container_->StackAboveWidget(frame_container_);
gfx::Path path;
GetContentsMask(contents_rect, &path);
html_container_->SetShape(path.CreateNativeRegion());
close_button_->SetBoundsRect(GetCloseButtonBounds());
options_menu_button_->SetBoundsRect(GetOptionsButtonBounds());
source_label_->SetBoundsRect(GetLabelBounds());
}
void BalloonViewImpl::RepositionToBalloon() {
if (closed_)
return;
DCHECK(frame_container_);
DCHECK(html_container_);
DCHECK(balloon_);
if (!kAnimateEnabled) {
frame_container_->SetBounds(GetBoundsForFrameContainer());
gfx::Rect contents_rect = GetContentsRectangle();
html_container_->SetBounds(contents_rect);
html_contents_->SetPreferredSize(contents_rect.size());
content::RenderWidgetHostView* view =
html_contents_->web_contents()->GetRenderWidgetHostView();
if (view)
view->SetSize(contents_rect.size());
return;
}
anim_frame_end_ = GetBoundsForFrameContainer();
anim_frame_start_ = frame_container_->GetClientAreaBoundsInScreen();
animation_.reset(new gfx::SlideAnimation(this));
animation_->Show();
}
void BalloonViewImpl::Update() {
if (closed_)
return;
if (!html_contents_.get() || !html_contents_->web_contents())
return;
html_contents_->web_contents()->GetController().LoadURL(
balloon_->notification().content_url(), content::Referrer(),
content::PAGE_TRANSITION_LINK, std::string());
}
void BalloonViewImpl::AnimationProgressed(const gfx::Animation* animation) {
DCHECK_EQ(animation_.get(), animation);
gfx::Rect frame_position(animation_->CurrentValueBetween(
anim_frame_start_, anim_frame_end_));
frame_container_->SetBounds(frame_position);
gfx::Path path;
gfx::Rect contents_rect = GetContentsRectangle();
html_container_->SetBounds(contents_rect);
GetContentsMask(contents_rect, &path);
html_container_->SetShape(path.CreateNativeRegion());
html_contents_->SetPreferredSize(contents_rect.size());
content::RenderWidgetHostView* view =
html_contents_->web_contents()->GetRenderWidgetHostView();
if (view)
view->SetSize(contents_rect.size());
}
gfx::Rect BalloonViewImpl::GetCloseButtonBounds() const {
gfx::Rect bounds(GetContentsBounds());
bounds.set_height(GetShelfHeight());
const gfx::Size& pref_size(close_button_->GetPreferredSize());
bounds.Inset(bounds.width() - kShelfMargin - pref_size.width(), 0,
kShelfMargin, 0);
bounds.ClampToCenteredSize(pref_size);
return bounds;
}
gfx::Rect BalloonViewImpl::GetOptionsButtonBounds() const {
gfx::Rect bounds(GetContentsBounds());
bounds.set_height(GetShelfHeight());
const gfx::Size& pref_size(options_menu_button_->GetPreferredSize());
bounds.set_x(GetCloseButtonBounds().x() - kOptionsDismissSpacing -
pref_size.width());
bounds.set_width(pref_size.width());
bounds.ClampToCenteredSize(pref_size);
return bounds;
}
gfx::Rect BalloonViewImpl::GetLabelBounds() const {
gfx::Rect bounds(GetContentsBounds());
bounds.set_height(GetShelfHeight());
gfx::Size pref_size(source_label_->GetPreferredSize());
bounds.Inset(kLabelLeftMargin, 0, bounds.width() -
GetOptionsButtonBounds().x() + kLabelOptionsSpacing, 0);
pref_size.set_width(bounds.width());
bounds.ClampToCenteredSize(pref_size);
return bounds;
}
void BalloonViewImpl::Show(Balloon* balloon) {
if (closed_)
return;
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
balloon_ = balloon;
const base::string16 source_label_text = l10n_util::GetStringFUTF16(
IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
balloon->notification().display_source());
source_label_ = new views::Label(source_label_text);
AddChildView(source_label_);
options_menu_button_ =
new views::MenuButton(NULL, base::string16(), this, false);
AddChildView(options_menu_button_);
close_button_ = new views::ImageButton(this);
close_button_->SetTooltipText(l10n_util::GetStringUTF16(
IDS_NOTIFICATION_BALLOON_DISMISS_LABEL));
AddChildView(close_button_);
html_contents_.reset(new BalloonViewHost(balloon));
html_contents_->SetPreferredSize(gfx::Size(10000, 10000));
if (enable_web_ui_)
html_contents_->EnableWebUI();
html_container_ = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
html_container_->Init(params);
html_container_->SetContentsView(html_contents_->view());
frame_container_ = new views::Widget;
params.delegate = this;
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.bounds = GetBoundsForFrameContainer();
frame_container_->Init(params);
frame_container_->SetContentsView(this);
frame_container_->StackAboveWidget(html_container_);
html_container_->SetBounds(GetContentsRectangle());
html_container_->SetAlwaysOnTop(true);
frame_container_->SetAlwaysOnTop(true);
close_button_->SetImage(views::CustomButton::STATE_NORMAL,
rb.GetImageSkiaNamed(IDR_CLOSE_1));
close_button_->SetImage(views::CustomButton::STATE_HOVERED,
rb.GetImageSkiaNamed(IDR_CLOSE_1_H));
close_button_->SetImage(views::CustomButton::STATE_PRESSED,
rb.GetImageSkiaNamed(IDR_CLOSE_1_P));
close_button_->SetBoundsRect(GetCloseButtonBounds());
close_button_->SetBackground(SK_ColorBLACK,
rb.GetImageSkiaNamed(IDR_CLOSE_1),
rb.GetImageSkiaNamed(IDR_CLOSE_1_MASK));
options_menu_button_->SetIcon(*rb.GetImageSkiaNamed(IDR_BALLOON_WRENCH));
options_menu_button_->SetHoverIcon(
*rb.GetImageSkiaNamed(IDR_BALLOON_WRENCH_H));
options_menu_button_->SetPushedIcon(*rb.GetImageSkiaNamed(
IDR_BALLOON_WRENCH_P));
options_menu_button_->set_alignment(views::TextButton::ALIGN_CENTER);
options_menu_button_->SetBorder(views::Border::NullBorder());
options_menu_button_->SetBoundsRect(GetOptionsButtonBounds());
source_label_->SetFontList(rb.GetFontList(ui::ResourceBundle::SmallFont));
source_label_->SetBackgroundColor(kControlBarBackgroundColor);
source_label_->SetEnabledColor(kControlBarTextColor);
source_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
source_label_->SetBoundsRect(GetLabelBounds());
SizeContentsWindow();
html_container_->Show();
frame_container_->Show();
notification_registrar_.Add(
this, chrome::NOTIFICATION_NOTIFY_BALLOON_DISCONNECTED,
content::Source<Balloon>(balloon));
}
void BalloonViewImpl::CreateOptionsMenu() {
if (options_menu_model_.get())
return;
options_menu_model_.reset(new NotificationOptionsMenuModel(balloon_));
}
void BalloonViewImpl::GetContentsMask(const gfx::Rect& rect,
gfx::Path* path) const {
SkScalar radius = SkIntToScalar(views::BubbleBorder::GetCornerRadius());
SkScalar spline_radius = radius -
SkScalarMul(radius, (SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3);
SkScalar left = SkIntToScalar(0);
SkScalar top = SkIntToScalar(0);
SkScalar right = SkIntToScalar(rect.width());
SkScalar bottom = SkIntToScalar(rect.height());
path->moveTo(left, top);
path->lineTo(right, top);
path->lineTo(right, bottom - radius);
path->cubicTo(right, bottom - spline_radius,
right - spline_radius, bottom,
right - radius, bottom);
path->lineTo(left + radius, bottom);
path->cubicTo(left + spline_radius, bottom,
left, bottom - spline_radius,
left, bottom - radius);
path->lineTo(left, top);
path->close();
}
void BalloonViewImpl::GetFrameMask(const gfx::Rect& rect,
gfx::Path* path) const {
SkScalar radius = SkIntToScalar(views::BubbleBorder::GetCornerRadius());
SkScalar spline_radius = radius -
SkScalarMul(radius, (SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3);
SkScalar left = SkIntToScalar(rect.x());
SkScalar top = SkIntToScalar(rect.y());
SkScalar right = SkIntToScalar(rect.right());
SkScalar bottom = SkIntToScalar(rect.bottom());
path->moveTo(left, bottom);
path->lineTo(left, top + radius);
path->cubicTo(left, top + spline_radius,
left + spline_radius, top,
left + radius, top);
path->lineTo(right - radius, top);
path->cubicTo(right - spline_radius, top,
right, top + spline_radius,
right, top + radius);
path->lineTo(right, bottom);
path->lineTo(left, bottom);
path->close();
}
gfx::Point BalloonViewImpl::GetContentsOffset() const {
return gfx::Point(kLeftShadowWidth + kLeftMargin,
kTopShadowWidth + kTopMargin);
}
gfx::Rect BalloonViewImpl::GetBoundsForFrameContainer() const {
return gfx::Rect(balloon_->GetPosition().x(), balloon_->GetPosition().y(),
GetTotalWidth(), GetTotalHeight());
}
int BalloonViewImpl::GetShelfHeight() const {
int max_button_height = std::max(std::max(
close_button_->GetPreferredSize().height(),
options_menu_button_->GetPreferredSize().height()),
source_label_->GetPreferredSize().height());
return max_button_height + kShelfMargin * 2;
}
int BalloonViewImpl::GetBalloonFrameHeight() const {
return GetTotalHeight() - GetShelfHeight();
}
int BalloonViewImpl::GetTotalWidth() const {
return balloon_->content_size().width() +
kLeftMargin + kRightMargin + kLeftShadowWidth + kRightShadowWidth;
}
int BalloonViewImpl::GetTotalHeight() const {
return balloon_->content_size().height() +
kTopMargin + kBottomMargin + kTopShadowWidth + kBottomShadowWidth +
GetShelfHeight();
}
gfx::Rect BalloonViewImpl::GetContentsRectangle() const {
if (!frame_container_)
return gfx::Rect();
gfx::Size content_size = balloon_->content_size();
gfx::Point offset = GetContentsOffset();
gfx::Rect frame_rect = frame_container_->GetWindowBoundsInScreen();
return gfx::Rect(frame_rect.x() + offset.x(),
frame_rect.y() + GetShelfHeight() + offset.y(),
content_size.width(),
content_size.height());
}
void BalloonViewImpl::OnPaint(gfx::Canvas* canvas) {
DCHECK(canvas);
gfx::Path path;
gfx::Rect rect = GetContentsBounds();
rect.set_height(GetShelfHeight());
GetFrameMask(rect, &path);
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(kControlBarBackgroundColor);
canvas->DrawPath(path, paint);
int line_width = GetTotalWidth() - kLeftMargin - kRightMargin;
canvas->FillRect(gfx::Rect(kLeftMargin, rect.bottom(), line_width, 1),
kControlBarSeparatorLineColor);
View::OnPaint(canvas);
OnPaintBorder(canvas);
}
void BalloonViewImpl::OnBoundsChanged(const gfx::Rect& previous_bounds) {
SizeContentsWindow();
}
void BalloonViewImpl::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type != chrome::NOTIFICATION_NOTIFY_BALLOON_DISCONNECTED) {
NOTREACHED();
return;
}
notification_registrar_.Remove(
this, chrome::NOTIFICATION_NOTIFY_BALLOON_DISCONNECTED,
content::Source<Balloon>(balloon_));
Close(false);
}