OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 "ui/message_center/views/notification_view_md.h" | 5 #include "ui/message_center/views/notification_view_md.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include "base/i18n/case_conversion.h" | 9 #include "base/i18n/case_conversion.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
11 #include "ui/base/cursor/cursor.h" | 11 #include "ui/base/cursor/cursor.h" |
12 #include "ui/base/l10n/l10n_util.h" | 12 #include "ui/base/l10n/l10n_util.h" |
13 #include "ui/gfx/canvas.h" | 13 #include "ui/gfx/canvas.h" |
14 #include "ui/gfx/geometry/size.h" | 14 #include "ui/gfx/geometry/size.h" |
| 15 #include "ui/gfx/image/image_skia_operations.h" |
15 #include "ui/gfx/paint_vector_icon.h" | 16 #include "ui/gfx/paint_vector_icon.h" |
16 #include "ui/gfx/skia_util.h" | 17 #include "ui/gfx/skia_util.h" |
17 #include "ui/gfx/text_elider.h" | 18 #include "ui/gfx/text_elider.h" |
18 #include "ui/message_center/message_center.h" | 19 #include "ui/message_center/message_center.h" |
19 #include "ui/message_center/message_center_style.h" | 20 #include "ui/message_center/message_center_style.h" |
20 #include "ui/message_center/notification.h" | 21 #include "ui/message_center/notification.h" |
21 #include "ui/message_center/notification_types.h" | 22 #include "ui/message_center/notification_types.h" |
22 #include "ui/message_center/vector_icons.h" | 23 #include "ui/message_center/vector_icons.h" |
23 #include "ui/message_center/views/bounded_label.h" | 24 #include "ui/message_center/views/bounded_label.h" |
24 #include "ui/message_center/views/constants.h" | 25 #include "ui/message_center/views/constants.h" |
(...skipping 17 matching lines...) Expand all Loading... |
42 #include "ui/views/widget/widget.h" | 43 #include "ui/views/widget/widget.h" |
43 | 44 |
44 namespace message_center { | 45 namespace message_center { |
45 | 46 |
46 namespace { | 47 namespace { |
47 | 48 |
48 // Dimensions. | 49 // Dimensions. |
49 constexpr gfx::Insets kContentRowPadding(4, 12, 12, 12); | 50 constexpr gfx::Insets kContentRowPadding(4, 12, 12, 12); |
50 constexpr gfx::Insets kActionsRowPadding(8, 8, 8, 8); | 51 constexpr gfx::Insets kActionsRowPadding(8, 8, 8, 8); |
51 constexpr int kActionsRowHorizontalSpacing = 8; | 52 constexpr int kActionsRowHorizontalSpacing = 8; |
52 constexpr gfx::Insets kImageContainerPadding(0, 12, 12, 12); | |
53 constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12); | 53 constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12); |
54 constexpr gfx::Insets kStatusTextPadding(4, 0, 0, 0); | 54 constexpr gfx::Insets kStatusTextPadding(4, 0, 0, 0); |
55 constexpr gfx::Size kActionButtonMinSize(88, 32); | 55 constexpr gfx::Size kActionButtonMinSize(88, 32); |
56 constexpr gfx::Size kIconViewSize(30, 30); | 56 constexpr gfx::Size kIconViewSize(30, 30); |
| 57 constexpr gfx::Insets kLargeImageContainerPadding(0, 12, 12, 12); |
| 58 constexpr gfx::Size kLargeImageMinSize(328, 0); |
| 59 constexpr gfx::Size kLargeImageMaxSize(328, 218); |
57 | 60 |
58 // Background of inline actions area. | 61 // Background of inline actions area. |
59 const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee); | 62 const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee); |
60 // Base ink drop color of action buttons. | 63 // Base ink drop color of action buttons. |
61 const SkColor kActionButtonInkDropBaseColor = SkColorSetRGB(0x0, 0x0, 0x0); | 64 const SkColor kActionButtonInkDropBaseColor = SkColorSetRGB(0x0, 0x0, 0x0); |
62 // Ripple ink drop opacity of action buttons. | 65 // Ripple ink drop opacity of action buttons. |
63 const float kActionButtonInkDropRippleVisibleOpacity = 0.08f; | 66 const float kActionButtonInkDropRippleVisibleOpacity = 0.08f; |
64 // Highlight (hover) ink drop opacity of action buttons. | 67 // Highlight (hover) ink drop opacity of action buttons. |
65 const float kActionButtonInkDropHighlightVisibleOpacity = 0.08f; | 68 const float kActionButtonInkDropHighlightVisibleOpacity = 0.08f; |
66 // Text color of action button. | 69 // Text color of action button. |
67 const SkColor kActionButtonTextColor = SkColorSetRGB(0x33, 0x67, 0xD6); | 70 const SkColor kActionButtonTextColor = SkColorSetRGB(0x33, 0x67, 0xD6); |
| 71 // Background color of the large image. |
| 72 const SkColor kLargeImageBackgroundColor = SkColorSetRGB(0xf5, 0xf5, 0xf5); |
68 | 73 |
69 // Max number of lines for message_view_. | 74 // Max number of lines for message_view_. |
70 constexpr int kMaxLinesForMessageView = 1; | 75 constexpr int kMaxLinesForMessageView = 1; |
71 constexpr int kMaxLinesForExpandedMessageView = 4; | 76 constexpr int kMaxLinesForExpandedMessageView = 4; |
72 | 77 |
73 constexpr int kCompactTitleMessageViewSpacing = 12; | 78 constexpr int kCompactTitleMessageViewSpacing = 12; |
74 | 79 |
75 constexpr int kProgressBarHeight = 4; | 80 constexpr int kProgressBarHeight = 4; |
76 | 81 |
77 constexpr int kMessageViewWidth = | 82 constexpr int kMessageViewWidth = |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 } | 253 } |
249 | 254 |
250 std::unique_ptr<views::InkDropHighlight> | 255 std::unique_ptr<views::InkDropHighlight> |
251 NotificationButtonMD::CreateInkDropHighlight() const { | 256 NotificationButtonMD::CreateInkDropHighlight() const { |
252 std::unique_ptr<views::InkDropHighlight> highlight = | 257 std::unique_ptr<views::InkDropHighlight> highlight = |
253 views::LabelButton::CreateInkDropHighlight(); | 258 views::LabelButton::CreateInkDropHighlight(); |
254 highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity); | 259 highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity); |
255 return highlight; | 260 return highlight; |
256 } | 261 } |
257 | 262 |
| 263 // LargeImageView ////////////////////////////////////////////////////////////// |
| 264 |
| 265 class LargeImageView : public views::View { |
| 266 public: |
| 267 LargeImageView(); |
| 268 ~LargeImageView() override; |
| 269 |
| 270 void SetImage(const gfx::ImageSkia& image); |
| 271 |
| 272 void OnPaint(gfx::Canvas* canvas) override; |
| 273 const char* GetClassName() const override; |
| 274 |
| 275 private: |
| 276 gfx::Size GetResizedImageSize(); |
| 277 |
| 278 gfx::ImageSkia image_; |
| 279 |
| 280 DISALLOW_COPY_AND_ASSIGN(LargeImageView); |
| 281 }; |
| 282 |
| 283 LargeImageView::LargeImageView() { |
| 284 SetBackground(views::CreateSolidBackground(kLargeImageBackgroundColor)); |
| 285 } |
| 286 |
| 287 LargeImageView::~LargeImageView() = default; |
| 288 |
| 289 void LargeImageView::SetImage(const gfx::ImageSkia& image) { |
| 290 image_ = image; |
| 291 gfx::Size preferred_size = GetResizedImageSize(); |
| 292 preferred_size.SetToMax(kLargeImageMinSize); |
| 293 preferred_size.SetToMin(kLargeImageMaxSize); |
| 294 SetPreferredSize(preferred_size); |
| 295 SchedulePaint(); |
| 296 Layout(); |
| 297 } |
| 298 |
| 299 void LargeImageView::OnPaint(gfx::Canvas* canvas) { |
| 300 views::View::OnPaint(canvas); |
| 301 |
| 302 gfx::Size resized_size = GetResizedImageSize(); |
| 303 gfx::Size drawn_size = resized_size; |
| 304 drawn_size.SetToMin(kLargeImageMaxSize); |
| 305 gfx::Rect drawn_bounds = GetContentsBounds(); |
| 306 drawn_bounds.ClampToCenteredSize(drawn_size); |
| 307 |
| 308 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage( |
| 309 image_, skia::ImageOperations::RESIZE_BEST, resized_size); |
| 310 |
| 311 // Cut off the overflown part. |
| 312 gfx::ImageSkia drawn_image = gfx::ImageSkiaOperations::ExtractSubset( |
| 313 resized_image, gfx::Rect(drawn_size)); |
| 314 |
| 315 canvas->DrawImageInt(drawn_image, drawn_bounds.x(), drawn_bounds.y()); |
| 316 } |
| 317 |
| 318 const char* LargeImageView::GetClassName() const { |
| 319 return "LargeImageView"; |
| 320 } |
| 321 |
| 322 // Returns expected size of the image right after resizing. |
| 323 // The GetResizedImageSize().width() <= kLargeImageMaxSize.width() holds, but |
| 324 // GetResizedImageSize().height() may be larger than kLargeImageMaxSize.height() |
| 325 // In this case, the overflown part will be just cutted off from the view. |
| 326 gfx::Size LargeImageView::GetResizedImageSize() { |
| 327 gfx::Size original_size = image_.size(); |
| 328 if (original_size.width() <= kLargeImageMaxSize.width()) |
| 329 return image_.size(); |
| 330 |
| 331 const double proportion = |
| 332 original_size.height() / static_cast<double>(original_size.width()); |
| 333 gfx::Size resized_size; |
| 334 resized_size.SetSize(kLargeImageMaxSize.width(), |
| 335 kLargeImageMaxSize.width() * proportion); |
| 336 return resized_size; |
| 337 } |
| 338 |
| 339 // LargeImageContainerView ///////////////////////////////////////////////////// |
| 340 |
| 341 // We have a container view outside LargeImageView, because we want to fill |
| 342 // area that is not coverted by the image by background color. |
| 343 class LargeImageContainerView : public views::View { |
| 344 public: |
| 345 LargeImageContainerView(); |
| 346 ~LargeImageContainerView() override; |
| 347 |
| 348 void SetImage(const gfx::ImageSkia& image); |
| 349 const char* GetClassName() const override; |
| 350 |
| 351 private: |
| 352 LargeImageView* const image_view_; |
| 353 |
| 354 DISALLOW_COPY_AND_ASSIGN(LargeImageContainerView); |
| 355 }; |
| 356 |
| 357 LargeImageContainerView::LargeImageContainerView() |
| 358 : image_view_(new LargeImageView()) { |
| 359 SetLayoutManager(new views::FillLayout()); |
| 360 SetBorder(views::CreateEmptyBorder(kLargeImageContainerPadding)); |
| 361 SetBackground( |
| 362 views::CreateSolidBackground(message_center::kImageBackgroundColor)); |
| 363 AddChildView(image_view_); |
| 364 } |
| 365 |
| 366 LargeImageContainerView::~LargeImageContainerView() = default; |
| 367 |
| 368 void LargeImageContainerView::SetImage(const gfx::ImageSkia& image) { |
| 369 image_view_->SetImage(image); |
| 370 } |
| 371 |
| 372 const char* LargeImageContainerView::GetClassName() const { |
| 373 return "LargeImageContainerView"; |
| 374 } |
| 375 |
258 } // anonymous namespace | 376 } // anonymous namespace |
259 | 377 |
260 // //////////////////////////////////////////////////////////// | 378 // //////////////////////////////////////////////////////////// |
261 // NotificationViewMD | 379 // NotificationViewMD |
262 // //////////////////////////////////////////////////////////// | 380 // //////////////////////////////////////////////////////////// |
263 | 381 |
264 views::View* NotificationViewMD::TargetForRect(views::View* root, | 382 views::View* NotificationViewMD::TargetForRect(views::View* root, |
265 const gfx::Rect& rect) { | 383 const gfx::Rect& rect) { |
266 CHECK_EQ(root, this); | 384 CHECK_EQ(root, this); |
267 | 385 |
(...skipping 362 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 list_items_count_ = items.size(); | 748 list_items_count_ = items.size(); |
631 | 749 |
632 // Needed when CreateOrUpdateViews is called for update. | 750 // Needed when CreateOrUpdateViews is called for update. |
633 if (!item_views_.empty()) | 751 if (!item_views_.empty()) |
634 left_content_->InvalidateLayout(); | 752 left_content_->InvalidateLayout(); |
635 } | 753 } |
636 | 754 |
637 void NotificationViewMD::CreateOrUpdateIconView( | 755 void NotificationViewMD::CreateOrUpdateIconView( |
638 const Notification& notification) { | 756 const Notification& notification) { |
639 if (notification.type() == NOTIFICATION_TYPE_PROGRESS || | 757 if (notification.type() == NOTIFICATION_TYPE_PROGRESS || |
640 notification.type() == NOTIFICATION_TYPE_MULTIPLE) { | 758 notification.type() == NOTIFICATION_TYPE_MULTIPLE || |
| 759 notification.type() == NOTIFICATION_TYPE_IMAGE) { |
641 DCHECK(!icon_view_ || right_content_->Contains(icon_view_)); | 760 DCHECK(!icon_view_ || right_content_->Contains(icon_view_)); |
642 delete icon_view_; | 761 delete icon_view_; |
643 icon_view_ = nullptr; | 762 icon_view_ = nullptr; |
644 return; | 763 return; |
645 } | 764 } |
646 | 765 |
647 if (!icon_view_) { | 766 if (!icon_view_) { |
648 icon_view_ = new ProportionalImageView(kIconViewSize); | 767 icon_view_ = new ProportionalImageView(kIconViewSize); |
649 right_content_->AddChildView(icon_view_); | 768 right_content_->AddChildView(icon_view_); |
650 } | 769 } |
651 | 770 |
652 gfx::ImageSkia icon = notification.icon().AsImageSkia(); | 771 gfx::ImageSkia icon = notification.icon().AsImageSkia(); |
653 icon_view_->SetImage(icon, icon.size()); | 772 icon_view_->SetImage(icon, icon.size()); |
654 } | 773 } |
655 | 774 |
656 void NotificationViewMD::CreateOrUpdateSmallIconView( | 775 void NotificationViewMD::CreateOrUpdateSmallIconView( |
657 const Notification& notification) { | 776 const Notification& notification) { |
658 if (notification.small_image().IsEmpty()) | 777 if (notification.small_image().IsEmpty()) |
659 header_row_->ClearAppIcon(); | 778 header_row_->ClearAppIcon(); |
660 else | 779 else |
661 header_row_->SetAppIcon(notification.small_image().AsImageSkia()); | 780 header_row_->SetAppIcon(notification.small_image().AsImageSkia()); |
662 } | 781 } |
663 | 782 |
664 void NotificationViewMD::CreateOrUpdateImageView( | 783 void NotificationViewMD::CreateOrUpdateImageView( |
665 const Notification& notification) { | 784 const Notification& notification) { |
666 // |image_view_| is the view representing the area covered by the | |
667 // notification's image, including background and border. Its size can be | |
668 // specified in advance and images will be scaled to fit including a border if | |
669 // necessary. | |
670 if (notification.image().IsEmpty()) { | 785 if (notification.image().IsEmpty()) { |
671 if (image_container_) { | 786 if (image_container_view_) { |
672 DCHECK(image_view_); | 787 DCHECK(Contains(image_container_view_)); |
673 DCHECK(Contains(image_container_)); | 788 delete image_container_view_; |
674 delete image_container_; | 789 image_container_view_ = nullptr; |
675 image_container_ = NULL; | |
676 image_view_ = NULL; | |
677 } else { | |
678 DCHECK(!image_view_); | |
679 } | 790 } |
680 return; | 791 return; |
681 } | 792 } |
682 | 793 |
683 gfx::Size ideal_size(kNotificationPreferredImageWidth, | 794 if (!image_container_view_) { |
684 kNotificationPreferredImageHeight); | 795 image_container_view_ = new LargeImageContainerView(); |
685 | |
686 if (!image_container_) { | |
687 image_container_ = new views::View(); | |
688 image_container_->SetLayoutManager(new views::FillLayout()); | |
689 image_container_->SetBorder( | |
690 views::CreateEmptyBorder(kImageContainerPadding)); | |
691 image_container_->SetBackground( | |
692 views::CreateSolidBackground(message_center::kImageBackgroundColor)); | |
693 | |
694 DCHECK(!image_view_); | |
695 image_view_ = new message_center::ProportionalImageView(ideal_size); | |
696 image_container_->AddChildView(image_view_); | |
697 // Insert the created image container just after the |content_row_|. | 796 // Insert the created image container just after the |content_row_|. |
698 AddChildViewAt(image_container_, GetIndexOf(content_row_) + 1); | 797 AddChildViewAt(image_container_view_, GetIndexOf(content_row_) + 1); |
699 } | 798 } |
700 | 799 |
701 DCHECK(image_view_); | 800 image_container_view_->SetImage(notification.image().AsImageSkia()); |
702 image_view_->SetImage(notification.image().AsImageSkia(), ideal_size); | |
703 } | 801 } |
704 | 802 |
705 void NotificationViewMD::CreateOrUpdateActionButtonViews( | 803 void NotificationViewMD::CreateOrUpdateActionButtonViews( |
706 const Notification& notification) { | 804 const Notification& notification) { |
707 std::vector<ButtonInfo> buttons = notification.buttons(); | 805 std::vector<ButtonInfo> buttons = notification.buttons(); |
708 bool new_buttons = action_buttons_.size() != buttons.size(); | 806 bool new_buttons = action_buttons_.size() != buttons.size(); |
709 | 807 |
710 if (new_buttons || buttons.size() == 0) { | 808 if (new_buttons || buttons.size() == 0) { |
711 for (auto* item : action_buttons_) | 809 for (auto* item : action_buttons_) |
712 delete item; | 810 delete item; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
771 // Expandable if the message exceeds one line. | 869 // Expandable if the message exceeds one line. |
772 if (message_view_ && message_view_->visible() && | 870 if (message_view_ && message_view_->visible() && |
773 message_view_->GetLinesForWidthAndLimit(message_view_->width(), -1) > 1) { | 871 message_view_->GetLinesForWidthAndLimit(message_view_->width(), -1) > 1) { |
774 return true; | 872 return true; |
775 } | 873 } |
776 // Expandable if there is at least one inline action. | 874 // Expandable if there is at least one inline action. |
777 if (actions_row_->has_children()) | 875 if (actions_row_->has_children()) |
778 return true; | 876 return true; |
779 | 877 |
780 // Expandable if the notification has image. | 878 // Expandable if the notification has image. |
781 if (image_view_) | 879 if (image_container_view_) |
782 return true; | 880 return true; |
783 | 881 |
784 // Expandable if there are multiple list items. | 882 // Expandable if there are multiple list items. |
785 if (item_views_.size() > 1) | 883 if (item_views_.size() > 1) |
786 return true; | 884 return true; |
787 | 885 |
788 // TODO(fukino): Expandable if both progress bar and message exist. | 886 // TODO(fukino): Expandable if both progress bar and message exist. |
789 | 887 |
790 return false; | 888 return false; |
791 } | 889 } |
792 | 890 |
793 void NotificationViewMD::ToggleExpanded() { | 891 void NotificationViewMD::ToggleExpanded() { |
794 expanded_ = !expanded_; | 892 expanded_ = !expanded_; |
795 UpdateViewForExpandedState(expanded_); | 893 UpdateViewForExpandedState(expanded_); |
796 content_row_->InvalidateLayout(); | 894 content_row_->InvalidateLayout(); |
797 if (controller()) | 895 if (controller()) |
798 controller()->UpdateNotificationSize(notification_id()); | 896 controller()->UpdateNotificationSize(notification_id()); |
799 } | 897 } |
800 | 898 |
801 void NotificationViewMD::UpdateViewForExpandedState(bool expanded) { | 899 void NotificationViewMD::UpdateViewForExpandedState(bool expanded) { |
802 header_row_->SetExpanded(expanded); | 900 header_row_->SetExpanded(expanded); |
803 if (message_view_) { | 901 if (message_view_) { |
804 message_view_->SetLineLimit(expanded ? kMaxLinesForExpandedMessageView | 902 message_view_->SetLineLimit(expanded ? kMaxLinesForExpandedMessageView |
805 : kMaxLinesForMessageView); | 903 : kMaxLinesForMessageView); |
806 } | 904 } |
807 if (image_container_) | 905 if (image_container_view_) |
808 image_container_->SetVisible(expanded); | 906 image_container_view_->SetVisible(expanded); |
809 actions_row_->SetVisible(expanded && actions_row_->has_children()); | 907 actions_row_->SetVisible(expanded && actions_row_->has_children()); |
810 for (size_t i = kMaxLinesForMessageView; i < item_views_.size(); ++i) { | 908 for (size_t i = kMaxLinesForMessageView; i < item_views_.size(); ++i) { |
811 item_views_[i]->SetVisible(expanded); | 909 item_views_[i]->SetVisible(expanded); |
812 } | 910 } |
813 if (status_view_) | 911 if (status_view_) |
814 status_view_->SetVisible(expanded); | 912 status_view_->SetVisible(expanded); |
815 header_row_->SetOverflowIndicator( | 913 header_row_->SetOverflowIndicator( |
816 list_items_count_ - | 914 list_items_count_ - |
817 (expanded ? item_views_.size() : kMaxLinesForMessageView)); | 915 (expanded ? item_views_.size() : kMaxLinesForMessageView)); |
818 } | 916 } |
(...skipping 10 matching lines...) Expand all Loading... |
829 header_row_->SetControlButtonsVisible(target_visibility); | 927 header_row_->SetControlButtonsVisible(target_visibility); |
830 } | 928 } |
831 | 929 |
832 NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView() | 930 NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView() |
833 const { | 931 const { |
834 // TODO(yoshiki): have this view use NotificationControlButtonsView. | 932 // TODO(yoshiki): have this view use NotificationControlButtonsView. |
835 return nullptr; | 933 return nullptr; |
836 } | 934 } |
837 | 935 |
838 } // namespace message_center | 936 } // namespace message_center |
OLD | NEW |