gpu: Tweak Android WebGL test expectations
[chromium-blink-merge.git] / ui / message_center / views / notification_view.cc
blobabfc1efe689e541a0360b5ec0d8d3ddafa8ca886
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"
33 namespace {
35 // Dimensions.
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;
63 // static
64 views::Background* MakeBackground(
65 SkColor color = message_center::kNotificationBackgroundColor) {
66 return views::Background::CreateSolidBackground(color);
69 // static
70 views::Border* MakeEmptyBorder(int top, int left, int bottom, int right) {
71 return views::Border::CreateEmptyBorder(top, left, bottom, right);
74 // static
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);
81 // static
82 views::Border* MakeProgressBarBorder(int top, int bottom) {
83 return MakeEmptyBorder(top, kTextLeftPadding, bottom, kTextRightPadding);
86 // static
87 views::Border* MakeSeparatorBorder(int top, int left, SkColor color) {
88 return views::Border::CreateSolidSidedBorder(top, left, 0, 0, color);
91 // static
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;
96 if (widget) {
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()) {
105 SkBitmap alpha;
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) {
111 return true;
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 {
126 public:
127 ItemView(const message_center::NotificationItem& item);
128 virtual ~ItemView();
130 // Overridden from views::View:
131 virtual void SetVisible(bool visible) OVERRIDE;
133 private:
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);
146 AddChildView(title);
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();
156 SchedulePaint();
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 {
172 public:
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;
181 private:
182 gfx::Size GetImageSizeForWidth(int width);
184 gfx::ImageSkia image_;
186 DISALLOW_COPY_AND_ASSIGN(ProportionalImageView);
189 ProportionalImageView::ProportionalImageView(const gfx::ImageSkia& image)
190 : image_(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());
217 } else {
218 // Resize case
219 SkPaint paint;
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(),
225 true, paint);
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 {
238 public:
239 NotificationProgressBar();
240 virtual ~NotificationProgressBar();
242 private:
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());
260 return pref_size;
263 void NotificationProgressBar::OnPaint(gfx::Canvas* canvas) {
264 gfx::Rect content_bounds = GetContentsBounds();
266 // Draw background.
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);
277 // Draw slice.
278 const int slice_width =
279 static_cast<int>(content_bounds.width() * GetNormalizedValue() + 0.5);
280 if (slice_width < 1)
281 return;
283 gfx::Rect slice_bounds = content_bounds;
284 slice_bounds.set_width(slice_width);
285 SkPath slice_path;
286 slice_path.addRoundRect(gfx::RectToSkRect(slice_bounds),
287 message_center::kProgressBarCornerRadius,
288 message_center::kProgressBarCornerRadius);
289 SkPaint slice_paint;
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 {
300 public:
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;
316 private:
317 views::ImageView* icon_;
318 views::Label* title_;
321 NotificationButton::NotificationButton(views::ButtonListener* listener)
322 : views::CustomButton(listener),
323 icon_(NULL),
324 title_(NULL) {
325 set_focusable(true);
326 set_request_focus_on_press(false);
327 SetLayoutManager(
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) {
338 if (icon_ != NULL)
339 delete icon_; // This removes the icon from this view's children.
340 if (image.isNull()) {
341 icon_ = NULL;
342 } else {
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);
349 icon_->set_border(
350 MakeEmptyBorder(message_center::kButtonIconTopPadding, 0, 0, 0));
351 AddChildViewAt(icon_, 0);
355 void NotificationButton::SetTitle(const string16& title) {
356 if (title_ != NULL)
357 delete title_; // This removes the title from this view's children.
358 if (title.empty()) {
359 title_ = NULL;
360 } else {
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) {
394 set_background(
395 MakeBackground(message_center::kHoveredButtonBackgroundColor));
396 } else {
397 set_background(NULL);
401 } // namespace
403 namespace message_center {
405 // NotificationView ////////////////////////////////////////////////////////////
407 // static
408 MessageView* NotificationView::Create(const Notification& notification,
409 MessageCenter* message_center,
410 MessageCenterTray* tray,
411 bool expanded,
412 bool top_level) {
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:
419 break;
420 default:
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
425 // functionality.
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.
437 if (top_level)
438 return notification_view;
439 #endif
441 notification_view->CreateShadowBorder();
442 return notification_view;
445 NotificationView::NotificationView(const Notification& notification,
446 MessageCenter* message_center,
447 MessageCenterTray* tray,
448 bool expanded)
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
458 // close button.
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.
466 title_view_ = NULL;
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;
532 } else {
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.
543 image_view_ = NULL;
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.
597 if (message_view_) {
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.
621 if (message_view_)
622 message_view_->SetLineLimit(GetMessageLineLimit(width()));
624 // Background.
625 background_view_->SetBounds(insets.left(), insets.top(),
626 content_width, height() - insets.height());
628 // Top views.
629 int top_height = top_view_->GetHeightForWidth(content_width);
630 top_view_->SetBounds(insets.left(), insets.top(), content_width, top_height);
632 // Icon.
633 icon_view_->SetBounds(insets.left(), insets.top(), kIconSize, kIconSize);
635 // Bottom views.
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);
641 // Close button.
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());
646 // Expand button.
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);
680 return this;
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;
692 #endif
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);
701 return;
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);
711 if (image_view_)
712 image_view_->SetVisible(true);
713 expand_button()->SetVisible(false);
714 PreferredSizeChanged();
715 SchedulePaint();
717 return;
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() &&
728 (image_view_ ||
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.
747 if (title_view_) {
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