1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/message_center/views/notification_view.h"
7 #include "base/command_line.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "grit/ui_resources.h"
11 #include "ui/base/layout.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/base/text/text_elider.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/size.h"
16 #include "ui/gfx/skia_util.h"
17 #include "ui/message_center/message_center.h"
18 #include "ui/message_center/message_center_style.h"
19 #include "ui/message_center/message_center_switches.h"
20 #include "ui/message_center/message_center_util.h"
21 #include "ui/message_center/notification.h"
22 #include "ui/message_center/notification_types.h"
23 #include "ui/message_center/views/bounded_label.h"
24 #include "ui/native_theme/native_theme.h"
25 #include "ui/views/controls/button/image_button.h"
26 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h"
28 #include "ui/views/controls/progress_bar.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/layout/fill_layout.h"
31 #include "ui/views/widget/widget.h"
36 const int kIconSize
= message_center::kNotificationIconSize
;
37 const int kLegacyIconSize
= 40;
38 const int kTextLeftPadding
= kIconSize
+ message_center::kIconToTextPadding
;
39 const int kTextBottomPadding
= 12;
40 const int kTextRightPadding
= 23;
41 const int kItemTitleToMessagePadding
= 3;
42 const int kProgressBarWidth
= message_center::kNotificationWidth
-
43 kTextLeftPadding
- kTextRightPadding
;
44 const int kProgressBarBottomPadding
= 0;
45 const int kButtonVecticalPadding
= 0;
46 const int kButtonTitleTopPadding
= 0;
48 // Character limits: Displayed text will be subject to the line limits above,
49 // but we also remove trailing characters from text to reduce processing cost.
50 // Character limit = pixels per line * line limit / min. pixels per character.
51 const size_t kTitleCharacterLimit
=
52 message_center::kNotificationWidth
* message_center::kTitleLineLimit
/ 4;
53 const size_t kMessageCharacterLimit
=
54 message_center::kNotificationWidth
*
55 message_center::kMessageExpandedLineLimit
/ 3;
57 // Notification colors. The text background colors below are used only to keep
58 // view::Label from modifying the text color and will not actually be drawn.
59 // See view::Label's RecalculateColors() for details.
60 const SkColor kRegularTextBackgroundColor
= SK_ColorWHITE
;
61 const SkColor kDimTextBackgroundColor
= SK_ColorWHITE
;
64 views::Background
* MakeBackground(
65 SkColor color
= message_center::kNotificationBackgroundColor
) {
66 return views::Background::CreateSolidBackground(color
);
70 views::Border
* MakeEmptyBorder(int top
, int left
, int bottom
, int right
) {
71 return views::Border::CreateEmptyBorder(top
, left
, bottom
, right
);
75 views::Border
* MakeTextBorder(int padding
, int top
, int bottom
) {
76 // Split the padding between the top and the bottom, then add the extra space.
77 return MakeEmptyBorder(padding
/ 2 + top
, kTextLeftPadding
,
78 (padding
+ 1) / 2 + bottom
, kTextRightPadding
);
82 views::Border
* MakeProgressBarBorder(int top
, int bottom
) {
83 return MakeEmptyBorder(top
, kTextLeftPadding
, bottom
, kTextRightPadding
);
87 views::Border
* MakeSeparatorBorder(int top
, int left
, SkColor color
) {
88 return views::Border::CreateSolidSidedBorder(top
, left
, 0, 0, color
);
92 // Return true if and only if the image is null or has alpha.
93 bool HasAlpha(gfx::ImageSkia
& image
, views::Widget
* widget
) {
94 // Determine which bitmap to use.
95 ui::ScaleFactor factor
= ui::SCALE_FACTOR_100P
;
97 factor
= ui::GetScaleFactorForNativeView(widget
->GetNativeView());
98 if (factor
== ui::SCALE_FACTOR_NONE
)
99 factor
= ui::SCALE_FACTOR_100P
;
102 // Extract that bitmap's alpha and look for a non-opaque pixel there.
103 SkBitmap bitmap
= image
.GetRepresentation(factor
).sk_bitmap();
104 if (!bitmap
.isNull()) {
106 alpha
.setConfig(SkBitmap::kA1_Config
, bitmap
.width(), bitmap
.height(), 0);
107 bitmap
.extractAlpha(&alpha
);
108 for (int y
= 0; y
< bitmap
.height(); ++y
) {
109 for (int x
= 0; x
< bitmap
.width(); ++x
) {
110 if (alpha
.getColor(x
, y
) != SK_ColorBLACK
) {
117 // If no opaque pixel was found, return false unless the bitmap is empty.
118 return bitmap
.isNull();
121 // ItemView ////////////////////////////////////////////////////////////////////
123 // ItemViews are responsible for drawing each list notification item's title and
124 // message next to each other within a single column.
125 class ItemView
: public views::View
{
127 ItemView(const message_center::NotificationItem
& item
);
130 // Overridden from views::View:
131 virtual void SetVisible(bool visible
) OVERRIDE
;
134 DISALLOW_COPY_AND_ASSIGN(ItemView
);
137 ItemView::ItemView(const message_center::NotificationItem
& item
) {
138 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal
,
139 0, 0, kItemTitleToMessagePadding
));
141 views::Label
* title
= new views::Label(item
.title
);
142 title
->set_collapse_when_hidden(true);
143 title
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
144 title
->SetEnabledColor(message_center::kRegularTextColor
);
145 title
->SetBackgroundColor(kRegularTextBackgroundColor
);
148 views::Label
* message
= new views::Label(item
.message
);
149 message
->set_collapse_when_hidden(true);
150 message
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
151 message
->SetEnabledColor(message_center::kDimTextColor
);
152 message
->SetBackgroundColor(kDimTextBackgroundColor
);
153 AddChildView(message
);
155 PreferredSizeChanged();
159 ItemView::~ItemView() {
162 void ItemView::SetVisible(bool visible
) {
163 views::View::SetVisible(visible
);
164 for (int i
= 0; i
< child_count(); ++i
)
165 child_at(i
)->SetVisible(visible
);
168 // ProportionalImageView ///////////////////////////////////////////////////////
170 // ProportionalImageViews center their images to preserve their proportion.
171 class ProportionalImageView
: public views::View
{
173 ProportionalImageView(const gfx::ImageSkia
& image
);
174 virtual ~ProportionalImageView();
176 // Overridden from views::View:
177 virtual gfx::Size
GetPreferredSize() OVERRIDE
;
178 virtual int GetHeightForWidth(int width
) OVERRIDE
;
179 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
;
182 gfx::Size
GetImageSizeForWidth(int width
);
184 gfx::ImageSkia image_
;
186 DISALLOW_COPY_AND_ASSIGN(ProportionalImageView
);
189 ProportionalImageView::ProportionalImageView(const gfx::ImageSkia
& image
)
193 ProportionalImageView::~ProportionalImageView() {
196 gfx::Size
ProportionalImageView::GetPreferredSize() {
197 gfx::Size size
= GetImageSizeForWidth(image_
.width());
198 return gfx::Size(size
.width() + GetInsets().width(),
199 size
.height() + GetInsets().height());
202 int ProportionalImageView::GetHeightForWidth(int width
) {
203 return GetImageSizeForWidth(width
).height();
206 void ProportionalImageView::OnPaint(gfx::Canvas
* canvas
) {
207 views::View::OnPaint(canvas
);
209 gfx::Size
draw_size(GetImageSizeForWidth(width()));
210 if (!draw_size
.IsEmpty()) {
211 gfx::Rect draw_bounds
= GetContentsBounds();
212 draw_bounds
.ClampToCenteredSize(draw_size
);
214 gfx::Size
image_size(image_
.size());
215 if (image_size
== draw_size
) {
216 canvas
->DrawImageInt(image_
, draw_bounds
.x(), draw_bounds
.y());
220 paint
.setFilterBitmap(true);
221 canvas
->DrawImageInt(image_
, 0, 0,
222 image_size
.width(), image_size
.height(),
223 draw_bounds
.x(), draw_bounds
.y(),
224 draw_size
.width(), draw_size
.height(),
230 gfx::Size
ProportionalImageView::GetImageSizeForWidth(int width
) {
231 gfx::Size size
= visible() ? image_
.size() : gfx::Size();
232 return message_center::GetImageSizeForWidth(width
, size
);
235 // NotificationProgressBar /////////////////////////////////////////////////////
237 class NotificationProgressBar
: public views::ProgressBar
{
239 NotificationProgressBar();
240 virtual ~NotificationProgressBar();
243 // Overriden from View
244 virtual gfx::Size
GetPreferredSize() OVERRIDE
;
245 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
;
247 DISALLOW_COPY_AND_ASSIGN(NotificationProgressBar
);
250 NotificationProgressBar::NotificationProgressBar() {
253 NotificationProgressBar::~NotificationProgressBar() {
256 gfx::Size
NotificationProgressBar::GetPreferredSize() {
257 gfx::Size
pref_size(kProgressBarWidth
, message_center::kProgressBarThickness
);
258 gfx::Insets insets
= GetInsets();
259 pref_size
.Enlarge(insets
.width(), insets
.height());
263 void NotificationProgressBar::OnPaint(gfx::Canvas
* canvas
) {
264 gfx::Rect content_bounds
= GetContentsBounds();
267 SkPath background_path
;
268 background_path
.addRoundRect(gfx::RectToSkRect(content_bounds
),
269 message_center::kProgressBarCornerRadius
,
270 message_center::kProgressBarCornerRadius
);
271 SkPaint background_paint
;
272 background_paint
.setStyle(SkPaint::kFill_Style
);
273 background_paint
.setFlags(SkPaint::kAntiAlias_Flag
);
274 background_paint
.setColor(message_center::kProgressBarBackgroundColor
);
275 canvas
->DrawPath(background_path
, background_paint
);
278 const int slice_width
=
279 static_cast<int>(content_bounds
.width() * GetNormalizedValue() + 0.5);
283 gfx::Rect slice_bounds
= content_bounds
;
284 slice_bounds
.set_width(slice_width
);
286 slice_path
.addRoundRect(gfx::RectToSkRect(slice_bounds
),
287 message_center::kProgressBarCornerRadius
,
288 message_center::kProgressBarCornerRadius
);
290 slice_paint
.setStyle(SkPaint::kFill_Style
);
291 slice_paint
.setFlags(SkPaint::kAntiAlias_Flag
);
292 slice_paint
.setColor(message_center::kProgressBarSliceColor
);
293 canvas
->DrawPath(slice_path
, slice_paint
);
296 // NotificationButton //////////////////////////////////////////////////////////
298 // NotificationButtons render the action buttons of notifications.
299 class NotificationButton
: public views::CustomButton
{
301 NotificationButton(views::ButtonListener
* listener
);
302 virtual ~NotificationButton();
304 void SetIcon(const gfx::ImageSkia
& icon
);
305 void SetTitle(const string16
& title
);
307 // Overridden from views::View:
308 virtual gfx::Size
GetPreferredSize() OVERRIDE
;
309 virtual int GetHeightForWidth(int width
) OVERRIDE
;
310 virtual void OnFocus() OVERRIDE
;
311 virtual void OnPaintFocusBorder(gfx::Canvas
* canvas
) OVERRIDE
;
313 // Overridden from views::CustomButton:
314 virtual void StateChanged() OVERRIDE
;
317 views::ImageView
* icon_
;
318 views::Label
* title_
;
321 NotificationButton::NotificationButton(views::ButtonListener
* listener
)
322 : views::CustomButton(listener
),
326 set_request_focus_on_press(false);
328 new views::BoxLayout(views::BoxLayout::kHorizontal
,
329 message_center::kButtonHorizontalPadding
,
330 kButtonVecticalPadding
,
331 message_center::kButtonIconToTitlePadding
));
334 NotificationButton::~NotificationButton() {
337 void NotificationButton::SetIcon(const gfx::ImageSkia
& image
) {
339 delete icon_
; // This removes the icon from this view's children.
340 if (image
.isNull()) {
343 icon_
= new views::ImageView();
344 icon_
->SetImageSize(gfx::Size(message_center::kNotificationButtonIconSize
,
345 message_center::kNotificationButtonIconSize
));
346 icon_
->SetImage(image
);
347 icon_
->SetHorizontalAlignment(views::ImageView::LEADING
);
348 icon_
->SetVerticalAlignment(views::ImageView::LEADING
);
350 MakeEmptyBorder(message_center::kButtonIconTopPadding
, 0, 0, 0));
351 AddChildViewAt(icon_
, 0);
355 void NotificationButton::SetTitle(const string16
& title
) {
357 delete title_
; // This removes the title from this view's children.
361 title_
= new views::Label(title
);
362 title_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
363 title_
->SetEnabledColor(message_center::kRegularTextColor
);
364 title_
->SetBackgroundColor(kRegularTextBackgroundColor
);
365 title_
->set_border(MakeEmptyBorder(kButtonTitleTopPadding
, 0, 0, 0));
366 AddChildView(title_
);
368 SetAccessibleName(title
);
371 gfx::Size
NotificationButton::GetPreferredSize() {
372 return gfx::Size(message_center::kNotificationWidth
,
373 message_center::kButtonHeight
);
376 int NotificationButton::GetHeightForWidth(int width
) {
377 return message_center::kButtonHeight
;
380 void NotificationButton::OnFocus() {
381 views::CustomButton::OnFocus();
382 ScrollRectToVisible(GetLocalBounds());
385 void NotificationButton::OnPaintFocusBorder(gfx::Canvas
* canvas
) {
386 if (HasFocus() && (focusable() || IsAccessibilityFocusable())) {
387 canvas
->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
388 message_center::kFocusBorderColor
);
392 void NotificationButton::StateChanged() {
393 if (state() == STATE_HOVERED
|| state() == STATE_PRESSED
) {
395 MakeBackground(message_center::kHoveredButtonBackgroundColor
));
397 set_background(NULL
);
403 namespace message_center
{
405 // NotificationView ////////////////////////////////////////////////////////////
408 MessageView
* NotificationView::Create(const Notification
& notification
,
409 MessageCenter
* message_center
,
410 MessageCenterTray
* tray
,
413 switch (notification
.type()) {
414 case NOTIFICATION_TYPE_BASE_FORMAT
:
415 case NOTIFICATION_TYPE_IMAGE
:
416 case NOTIFICATION_TYPE_MULTIPLE
:
417 case NOTIFICATION_TYPE_SIMPLE
:
418 case NOTIFICATION_TYPE_PROGRESS
:
421 // If the caller asks for an unrecognized kind of view (entirely possible
422 // if an application is running on an older version of this code that
423 // doesn't have the requested kind of notification template), we'll fall
424 // back to a notification instance that will provide at least basic
426 LOG(WARNING
) << "Unable to fulfill request for unrecognized "
427 << "notification type " << notification
.type() << ". "
428 << "Falling back to simple notification type.";
431 // Currently all roads lead to the generic NotificationView.
432 MessageView
* notification_view
=
433 new NotificationView(notification
, message_center
, tray
, expanded
);
435 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
436 // Don't create shadows for notification toasts on linux wih aura.
438 return notification_view
;
441 notification_view
->CreateShadowBorder();
442 return notification_view
;
445 NotificationView::NotificationView(const Notification
& notification
,
446 MessageCenter
* message_center
,
447 MessageCenterTray
* tray
,
449 : MessageView(notification
, message_center
, tray
, expanded
) {
450 std::vector
<string16
> accessible_lines
;
452 // Create the opaque background that's above the view's shadow.
453 background_view_
= new views::View();
454 background_view_
->set_background(MakeBackground());
456 // Create the top_view_, which collects into a vertical box all content
457 // at the top of the notification (to the right of the icon) except for the
459 top_view_
= new views::View();
460 top_view_
->SetLayoutManager(
461 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
462 top_view_
->set_border(MakeEmptyBorder(
463 kTextTopPadding
- 8, 0, kTextBottomPadding
- 5, 0));
465 // Create the title view if appropriate.
467 if (!notification
.title().empty()) {
468 gfx::Font font
= views::Label().font().DeriveFont(2);
469 int padding
= kTitleLineHeight
- font
.GetHeight();
470 title_view_
= new BoundedLabel(
471 ui::TruncateString(notification
.title(), kTitleCharacterLimit
), font
);
472 title_view_
->SetLineHeight(kTitleLineHeight
);
473 title_view_
->SetLineLimit(message_center::kTitleLineLimit
);
474 title_view_
->SetColors(message_center::kRegularTextColor
,
475 kRegularTextBackgroundColor
);
476 title_view_
->set_border(MakeTextBorder(padding
, 3, 0));
477 top_view_
->AddChildView(title_view_
);
478 accessible_lines
.push_back(notification
.title());
481 // Create the message view if appropriate.
482 message_view_
= NULL
;
483 if (!notification
.message().empty()) {
484 int padding
= kMessageLineHeight
- views::Label().font().GetHeight();
485 message_view_
= new BoundedLabel(
486 ui::TruncateString(notification
.message(), kMessageCharacterLimit
));
487 message_view_
->SetLineHeight(kMessageLineHeight
);
488 message_view_
->SetVisible(!is_expanded() || !notification
.items().size());
489 message_view_
->SetColors(message_center::kDimTextColor
,
490 kDimTextBackgroundColor
);
491 message_view_
->set_border(MakeTextBorder(padding
, 4, 0));
492 top_view_
->AddChildView(message_view_
);
493 accessible_lines
.push_back(notification
.message());
496 // Create the progress bar view.
497 progress_bar_view_
= NULL
;
498 if (notification
.type() == NOTIFICATION_TYPE_PROGRESS
) {
499 progress_bar_view_
= new NotificationProgressBar();
500 progress_bar_view_
->set_border(MakeProgressBarBorder(
501 message_center::kProgressBarTopPadding
, kProgressBarBottomPadding
));
502 progress_bar_view_
->SetValue(notification
.progress() / 100.0);
503 top_view_
->AddChildView(progress_bar_view_
);
506 // Create the list item views (up to a maximum).
507 int padding
= kMessageLineHeight
- views::Label().font().GetHeight();
508 std::vector
<NotificationItem
> items
= notification
.items();
509 for (size_t i
= 0; i
< items
.size() && i
< kNotificationMaximumItems
; ++i
) {
510 ItemView
* item_view
= new ItemView(items
[i
]);
511 item_view
->SetVisible(is_expanded());
512 item_view
->set_border(MakeTextBorder(padding
, i
? 0 : 4, 0));
513 item_views_
.push_back(item_view
);
514 top_view_
->AddChildView(item_view
);
515 accessible_lines
.push_back(
516 items
[i
].title
+ base::ASCIIToUTF16(" ") + items
[i
].message
);
519 // Create the notification icon view.
520 gfx::ImageSkia icon
= notification
.icon().AsImageSkia();
521 if (notification
.type() == NOTIFICATION_TYPE_SIMPLE
&&
522 (icon
.width() != kIconSize
||
523 icon
.height() != kIconSize
||
524 HasAlpha(icon
, GetWidget()))) {
525 views::ImageView
* icon_view
= new views::ImageView();
526 icon_view
->SetImage(icon
);
527 icon_view
->SetImageSize(gfx::Size(kLegacyIconSize
, kLegacyIconSize
));
528 icon_view
->SetHorizontalAlignment(views::ImageView::CENTER
);
529 icon_view
->SetVerticalAlignment(views::ImageView::CENTER
);
530 icon_view
->set_background(MakeBackground(kLegacyIconBackgroundColor
));
531 icon_view_
= icon_view
;
533 icon_view_
= new ProportionalImageView(icon
);
536 // Create the bottom_view_, which collects into a vertical box all content
537 // below the notification icon except for the expand button.
538 bottom_view_
= new views::View();
539 bottom_view_
->SetLayoutManager(
540 new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
542 // Create the image view if appropriate.
544 if (!notification
.image().IsEmpty()) {
545 image_view_
= new ProportionalImageView(notification
.image().AsImageSkia());
546 image_view_
->SetVisible(is_expanded());
547 bottom_view_
->AddChildView(image_view_
);
550 // Create action buttons if appropriate.
551 std::vector
<ButtonInfo
> buttons
= notification
.buttons();
552 for (size_t i
= 0; i
< buttons
.size(); ++i
) {
553 views::View
* separator
= new views::ImageView();
554 separator
->set_border(MakeSeparatorBorder(1, 0, kButtonSeparatorColor
));
555 bottom_view_
->AddChildView(separator
);
556 NotificationButton
* button
= new NotificationButton(this);
557 ButtonInfo button_info
= buttons
[i
];
558 button
->SetTitle(button_info
.title
);
559 button
->SetIcon(button_info
.icon
.AsImageSkia());
560 action_buttons_
.push_back(button
);
561 bottom_view_
->AddChildView(button
);
564 // Put together the different content and control views. Layering those allows
565 // for proper layout logic and it also allows the close and expand buttons to
566 // overlap the content as needed to provide large enough click and touch areas
567 // (<http://crbug.com/168822> and <http://crbug.com/168856>).
568 AddChildView(background_view_
);
569 AddChildView(top_view_
);
570 AddChildView(icon_view_
);
571 AddChildView(bottom_view_
);
572 AddChildView(close_button());
573 AddChildView(expand_button());
574 set_accessible_name(JoinString(accessible_lines
, '\n'));
577 NotificationView::~NotificationView() {
580 gfx::Size
NotificationView::GetPreferredSize() {
581 int top_width
= top_view_
->GetPreferredSize().width();
582 int bottom_width
= bottom_view_
->GetPreferredSize().width();
583 int preferred_width
= std::max(top_width
, bottom_width
) + GetInsets().width();
584 return gfx::Size(preferred_width
, GetHeightForWidth(preferred_width
));
587 int NotificationView::GetHeightForWidth(int width
) {
588 // Get the height assuming no line limit changes.
589 int content_width
= width
- GetInsets().width();
590 int top_height
= top_view_
->GetHeightForWidth(content_width
);
591 int bottom_height
= bottom_view_
->GetHeightForWidth(content_width
);
592 int content_height
= std::max(top_height
, kIconSize
) + bottom_height
;
594 // <http://crbug.com/230448> Fix: Adjust the height when the message_view's
595 // line limit would be different for the specified width than it currently is.
596 // TODO(dharcourt): Avoid BoxLayout and directly compute the correct height.
598 int used_limit
= message_view_
->GetLineLimit();
599 int correct_limit
= GetMessageLineLimit(width
);
600 if (used_limit
!= correct_limit
) {
601 content_height
-= GetMessageHeight(content_width
, used_limit
);
602 content_height
+= GetMessageHeight(content_width
, correct_limit
);
606 // Adjust the height to make sure there is at least 16px of space below the
607 // icon if there is any space there (<http://crbug.com/232966>).
608 if (content_height
> kIconSize
)
609 content_height
= std::max(content_height
,
610 kIconSize
+ message_center::kIconBottomPadding
);
612 return content_height
+ GetInsets().height();
615 void NotificationView::Layout() {
616 gfx::Insets insets
= GetInsets();
617 int content_width
= width() - insets
.width();
618 int content_right
= width() - insets
.right();
620 // Before any resizing, set or adjust the number of message lines.
622 message_view_
->SetLineLimit(GetMessageLineLimit(width()));
625 background_view_
->SetBounds(insets
.left(), insets
.top(),
626 content_width
, height() - insets
.height());
629 int top_height
= top_view_
->GetHeightForWidth(content_width
);
630 top_view_
->SetBounds(insets
.left(), insets
.top(), content_width
, top_height
);
633 icon_view_
->SetBounds(insets
.left(), insets
.top(), kIconSize
, kIconSize
);
636 int bottom_y
= insets
.top() + std::max(top_height
, kIconSize
);
637 int bottom_height
= bottom_view_
->GetHeightForWidth(content_width
);
638 bottom_view_
->SetBounds(insets
.left(), bottom_y
,
639 content_width
, bottom_height
);
642 gfx::Size
close_size(close_button()->GetPreferredSize());
643 close_button()->SetBounds(content_right
- close_size
.width(), insets
.top(),
644 close_size
.width(), close_size
.height());
647 gfx::Size
expand_size(expand_button()->GetPreferredSize());
648 int expand_y
= bottom_y
- expand_size
.height();
649 expand_button()->SetVisible(IsExpansionNeeded(width()));
650 expand_button()->SetBounds(content_right
- expand_size
.width(), expand_y
,
651 expand_size
.width(), expand_size
.height());
654 void NotificationView::OnFocus() {
655 MessageView::OnFocus();
656 ScrollRectToVisible(GetLocalBounds());
659 void NotificationView::ScrollRectToVisible(const gfx::Rect
& rect
) {
660 // Notification want to show the whole notification when a part of it (like
661 // a button) gets focused.
662 views::View::ScrollRectToVisible(GetLocalBounds());
665 views::View
* NotificationView::GetEventHandlerForPoint(
666 const gfx::Point
& point
) {
667 // Want to return this for underlying views, otherwise GetCursor is not
668 // called. But buttons are exceptions, they'll have their own event handlings.
669 std::vector
<views::View
*> buttons(action_buttons_
);
670 buttons
.push_back(close_button());
671 buttons
.push_back(expand_button());
673 for (size_t i
= 0; i
< buttons
.size(); ++i
) {
674 gfx::Point point_in_child
= point
;
675 ConvertPointToTarget(this, buttons
[i
], &point_in_child
);
676 if (buttons
[i
]->HitTestPoint(point_in_child
))
677 return buttons
[i
]->GetEventHandlerForPoint(point_in_child
);
683 gfx::NativeCursor
NotificationView::GetCursor(const ui::MouseEvent
& event
) {
684 if (!message_center()->HasClickedListener(notification_id()))
685 return views::View::GetCursor(event
);
687 #if defined(USE_AURA)
688 return ui::kCursorHand
;
689 #elif defined(OS_WIN)
690 static HCURSOR g_hand_cursor
= LoadCursor(NULL
, IDC_HAND
);
691 return g_hand_cursor
;
695 void NotificationView::ButtonPressed(views::Button
* sender
,
696 const ui::Event
& event
) {
697 // See if the button pressed was an action button.
698 for (size_t i
= 0; i
< action_buttons_
.size(); ++i
) {
699 if (sender
== action_buttons_
[i
]) {
700 message_center()->ClickOnNotificationButton(notification_id(), i
);
705 // Adjust notification subviews for expansion.
706 if (sender
== expand_button()) {
707 if (message_view_
&& item_views_
.size())
708 message_view_
->SetVisible(false);
709 for (size_t i
= 0; i
< item_views_
.size(); ++i
)
710 item_views_
[i
]->SetVisible(true);
712 image_view_
->SetVisible(true);
713 expand_button()->SetVisible(false);
714 PreferredSizeChanged();
720 // Let the superclass handled anything other than action buttons.
721 // Warning: This may cause the NotificationView itself to be deleted,
722 // so don't do anything afterwards.
723 MessageView::ButtonPressed(sender
, event
);
726 bool NotificationView::IsExpansionNeeded(int width
) {
727 return (!is_expanded() &&
729 item_views_
.size() ||
730 IsMessageExpansionNeeded(width
)));
733 bool NotificationView::IsMessageExpansionNeeded(int width
) {
734 int current
= GetMessageLines(width
, GetMessageLineLimit(width
));
735 int expanded
= GetMessageLines(width
,
736 message_center::kMessageExpandedLineLimit
);
737 return current
< expanded
;
740 int NotificationView::GetMessageLineLimit(int width
) {
741 // Expanded notifications get a larger limit, except for image notifications,
742 // whose images must be kept flush against their icons.
743 if (is_expanded() && !image_view_
)
744 return message_center::kMessageExpandedLineLimit
;
746 // If there's a title ensure title + message lines <= collapsed line limit.
748 int title_lines
= title_view_
->GetLinesForWidthAndLimit(width
, -1);
749 return std::max(message_center::kMessageCollapsedLineLimit
- title_lines
,
753 return message_center::kMessageCollapsedLineLimit
;
756 int NotificationView::GetMessageLines(int width
, int limit
) {
757 return message_view_
?
758 message_view_
->GetLinesForWidthAndLimit(width
, limit
) : 0;
761 int NotificationView::GetMessageHeight(int width
, int limit
) {
762 return message_view_
?
763 message_view_
->GetSizeForWidthAndLines(width
, limit
).height() : 0;
766 } // namespace message_center