Chromium Code Reviews| Index: ash/common/system/web_notification/web_notification_tray.cc |
| diff --git a/ash/common/system/web_notification/web_notification_tray.cc b/ash/common/system/web_notification/web_notification_tray.cc |
| index feba9fffdf06e91fba9d42e6d2c0e151a1782607..bde98a579f43e017efea54f82b5c32d70f3bb229 100644 |
| --- a/ash/common/system/web_notification/web_notification_tray.cc |
| +++ b/ash/common/system/web_notification/web_notification_tray.cc |
| @@ -63,8 +63,24 @@ namespace ash { |
| namespace { |
| // Menu commands |
| -const int kToggleQuietMode = 0; |
| -const int kEnableQuietModeDay = 2; |
| +constexpr int kToggleQuietMode = 0; |
| +constexpr int kEnableQuietModeDay = 2; |
| + |
| +constexpr int kMaximumSmallIconCount = 3; |
| + |
| +constexpr gfx::Size kTrayItemInnerIconSize = gfx::Size(16, 16); |
| +constexpr gfx::Size kTrayItemInnerBellIconSize = gfx::Size(18, 18); |
|
oshima
2016/08/04 18:15:21
I think you can do
kTrayItemInnerIconSize(16, 16)
yoshiki
2016/08/05 13:30:47
Done.
|
| + |
| +constexpr int kTrayItemWidth = 26; |
| +constexpr int kTrayItemHeight = 26; |
|
oshima
2016/08/04 18:15:21
can this also be gfx::Size ?
yoshiki
2016/08/05 13:30:47
Done.
|
| +constexpr int kTrayItemHorizontalInset = 6; |
| +constexpr int kTrayItemVerticalInset = 6; |
| + |
| +constexpr int kTrayItemAnimationDurationMS = 200; |
| + |
| +static int round(double d) { |
| + return std::floor(d + 0.5); |
| +} |
|
oshima
2016/08/04 18:15:20
gfx::ToRoundedInt
yoshiki
2016/08/05 13:30:48
Done.
|
| } |
| namespace { |
| @@ -115,53 +131,66 @@ class WebNotificationBubbleWrapper { |
| DISALLOW_COPY_AND_ASSIGN(WebNotificationBubbleWrapper); |
| }; |
| -class WebNotificationButton : public views::CustomButton { |
| +class WebNotificationItem : public views::View, public gfx::AnimationDelegate { |
| public: |
| - WebNotificationButton(views::ButtonListener* listener) |
| - : views::CustomButton(listener), |
| - is_bubble_visible_(false), |
| - unread_count_(0) { |
| - SetLayoutManager(new views::FillLayout); |
| + WebNotificationItem(gfx::AnimationContainer* container, |
| + WebNotificationTray* tray) |
| + : tray_(tray) { |
| + SetPaintToLayer(true); |
| + layer()->SetFillsBoundsOpaquely(false); |
| + views::View::SetVisible(false); |
| + set_owned_by_client(); |
| - gfx::ImageSkia image; |
| - if (MaterialDesignController::IsShelfMaterial()) { |
| - image = CreateVectorIcon(gfx::VectorIconId::SHELF_NOTIFICATIONS, |
| - kShelfIconColor); |
| - } else { |
| - image = |
| - CreateVectorIcon(gfx::VectorIconId::NOTIFICATIONS, kNoUnreadIconSize, |
| - kWebNotificationColorNoUnread); |
| - } |
| - |
| - no_unread_icon_.SetImage(image); |
| - no_unread_icon_.set_owned_by_client(); |
| - |
| - unread_label_.set_owned_by_client(); |
| - SetupLabelForTray(&unread_label_); |
| + SetLayoutManager(new views::FillLayout); |
| - AddChildView(&no_unread_icon_); |
| + animation_.reset(new gfx::SlideAnimation(this)); |
| + animation_->SetContainer(container); |
| + animation_->SetSlideDuration(kTrayItemAnimationDurationMS); |
| + animation_->SetTweenType(gfx::Tween::LINEAR); |
| } |
| - void SetBubbleVisible(bool visible) { |
| - if (visible == is_bubble_visible_) |
| + void SetVisible(bool set_visible) override { |
| + if (!GetWidget()) { |
| + views::View::SetVisible(set_visible); |
| return; |
| + } |
| - is_bubble_visible_ = visible; |
| - UpdateIconVisibility(); |
| + if (!set_visible) { |
| + animation_->Hide(); |
| + AnimationProgressed(animation_.get()); |
| + } else { |
| + animation_->Show(); |
| + AnimationProgressed(animation_.get()); |
| + views::View::SetVisible(true); |
| + } |
| } |
| - void SetUnreadCount(int unread_count) { |
| - // base::FormatNumber doesn't convert to arabic numeric characters. |
| - // TODO(mukai): use ICU to support conversion for such locales. |
| - unread_count_ = unread_count; |
| - UpdateIconVisibility(); |
| + void HideAndDelete() { |
| + SetVisible(false); |
| + |
| + if (!visible() && !animation_->is_animating()) { |
| + if (parent()) |
| + parent()->RemoveChildView(this); |
| + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| + } else { |
| + delete_after_animation_ = true; |
| + } |
| } |
| protected: |
| // Overridden from views::ImageButton: |
| gfx::Size GetPreferredSize() const override { |
| - const int size = GetTrayConstant(TRAY_ITEM_HEIGHT_LEGACY); |
| - return gfx::Size(size, size); |
| + gfx::Size size = gfx::Size(kTrayItemWidth, kTrayItemHeight); |
| + if (!animation_.get() || !animation_->is_animating()) |
| + return size; |
| + if (IsHorizontalAlignment(tray_->shelf_alignment())) { |
|
oshima
2016/08/04 18:15:21
since it's used in many places now, it's probably
yoshiki
2016/08/05 13:30:48
Done.
|
| + size.set_width( |
| + std::max(1, round(size.width() * animation_->GetCurrentValue()))); |
| + } else { |
| + size.set_height( |
| + std::max(1, round(size.height() * animation_->GetCurrentValue()))); |
| + } |
| + return size; |
| } |
|
oshima
2016/08/04 18:15:21
I guess we can't just clip it because there are mu
yoshiki
2016/08/05 13:30:48
Done.
|
| int GetHeightForWidth(int width) const override { |
| @@ -169,38 +198,86 @@ class WebNotificationButton : public views::CustomButton { |
| } |
| private: |
| - void UpdateIconVisibility() { |
| - if (unread_count_ == 0) { |
| - if (!Contains(&no_unread_icon_)) { |
| - RemoveAllChildViews(false /* delete_children */); |
| - AddChildView(&no_unread_icon_); |
| - } |
| + // Overridden from gfx::AnimationDelegate. |
|
oshima
2016/08/04 18:15:21
// gfx::AnimationDelegate:
yoshiki
2016/08/05 13:30:47
Done.
|
| + void AnimationProgressed(const gfx::Animation* animation) override { |
| + gfx::Transform transform; |
| + if (IsHorizontalAlignment(tray_->shelf_alignment())) { |
| + transform.Translate(0, animation->CurrentValueBetween( |
| + static_cast<double>(height()) / 2, 0.)); |
| } else { |
| - if (!Contains(&unread_label_)) { |
| - RemoveAllChildViews(false /* delete_children */); |
| - AddChildView(&unread_label_); |
| - } |
| - |
| - // TODO(mukai): move NINE_PLUS message to ui_strings, it doesn't need to |
| - // be in ash_strings. |
| - unread_label_.SetText( |
| - (unread_count_ > 9) ? l10n_util::GetStringUTF16( |
| - IDS_ASH_NOTIFICATION_UNREAD_COUNT_NINE_PLUS) |
| - : base::FormatNumber(unread_count_)); |
| - unread_label_.SetEnabledColor((unread_count_ > 0) |
| - ? kWebNotificationColorWithUnread |
| - : kWebNotificationColorNoUnread); |
| + transform.Translate( |
| + animation->CurrentValueBetween(static_cast<double>(width() / 2), 0.), |
| + 0); |
| } |
| - SchedulePaint(); |
| + transform.Scale(animation->GetCurrentValue(), animation->GetCurrentValue()); |
| + layer()->SetTransform(transform); |
| + PreferredSizeChanged(); |
| + } |
| + void AnimationEnded(const gfx::Animation* animation) override { |
| + if (animation->GetCurrentValue() < 0.1) |
| + views::View::SetVisible(false); |
| + |
| + if (delete_after_animation_) { |
| + if (parent()) |
| + parent()->RemoveChildView(this); |
| + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| + } |
| + } |
| + void AnimationCanceled(const gfx::Animation* animation) override { |
| + AnimationEnded(animation); |
| + } |
| + |
| + std::unique_ptr<gfx::SlideAnimation> animation_; |
| + bool delete_after_animation_ = false; |
| + WebNotificationTray* tray_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(WebNotificationItem); |
| +}; |
| + |
| +class WebNotificationImage : public WebNotificationItem { |
| + public: |
| + WebNotificationImage(const gfx::ImageSkia& image, |
| + gfx::Size size, |
| + gfx::AnimationContainer* container, |
| + WebNotificationTray* tray) |
| + : WebNotificationItem(container, tray) { |
| + view_ = new views::ImageView(); |
| + view_->SetImage(image); |
| + view_->SetImageSize(size); |
| + AddChildView(view_); |
| } |
| - bool is_bubble_visible_; |
| - int unread_count_; |
| + private: |
| + views::ImageView* view_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(WebNotificationImage); |
| +}; |
| - views::ImageView no_unread_icon_; |
| - views::Label unread_label_; |
| +class WebNotificationLabel : public WebNotificationItem { |
| + public: |
| + WebNotificationLabel(gfx::AnimationContainer* container, |
| + WebNotificationTray* tray) |
| + : WebNotificationItem(container, tray) { |
| + view_ = new views::Label(); |
| + SetupLabelForTray(view_); |
| + } |
| - DISALLOW_COPY_AND_ASSIGN(WebNotificationButton); |
| + void SetUnreadCount(int unread_count) { |
| + // TODO(mukai): move NINE_PLUS message to ui_strings, it doesn't need to |
| + // be in ash_strings. |
| + view_->SetText((unread_count > 9) |
| + ? l10n_util::GetStringUTF16( |
| + IDS_ASH_NOTIFICATION_UNREAD_COUNT_NINE_PLUS) |
| + : base::FormatNumber(unread_count)); |
| + view_->SetEnabledColor(kWebNotificationColorWithUnread); |
| + AddChildView(view_); |
| + SchedulePaint(); |
| + } |
| + |
| + private: |
| + views::Label* view_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(WebNotificationLabel); |
| }; |
| WebNotificationTray::WebNotificationTray(WmShelf* shelf, |
| @@ -209,18 +286,30 @@ WebNotificationTray::WebNotificationTray(WmShelf* shelf, |
| : TrayBackgroundView(shelf), |
| status_area_window_(status_area_window), |
| system_tray_(system_tray), |
| - button_(nullptr), |
| show_message_center_on_unlock_(false), |
| should_update_tray_content_(false), |
| should_block_shelf_auto_hide_(false) { |
| DCHECK(shelf); |
| DCHECK(status_area_window_); |
| DCHECK(system_tray_); |
| - button_ = new WebNotificationButton(this); |
| - button_->set_triggerable_event_flags(ui::EF_LEFT_MOUSE_BUTTON | |
| - ui::EF_RIGHT_MOUSE_BUTTON); |
| - tray_container()->AddChildView(button_); |
| - button_->SetFocusBehavior(FocusBehavior::NEVER); |
| + |
| + gfx::ImageSkia bell_image; |
| + if (MaterialDesignController::IsShelfMaterial()) { |
| + bell_image = CreateVectorIcon(gfx::VectorIconId::SHELF_NOTIFICATIONS, |
| + kShelfIconColor); |
| + } else { |
| + bell_image = |
| + CreateVectorIcon(gfx::VectorIconId::NOTIFICATIONS, kNoUnreadIconSize, |
| + kWebNotificationColorNoUnread); |
| + } |
| + bell_icon_.reset(new WebNotificationImage(bell_image, |
| + kTrayItemInnerBellIconSize, |
| + animation_container_.get(), this)); |
| + tray_container()->AddChildView(bell_icon_.get()); |
| + |
| + counter_.reset(new WebNotificationLabel(animation_container_.get(), this)); |
| + tray_container()->AddChildView(counter_.get()); |
| + |
| SetContentsBackground(); |
| tray_container()->SetBorder(views::Border::NullBorder()); |
| message_center_tray_.reset(new message_center::MessageCenterTray( |
| @@ -234,6 +323,9 @@ WebNotificationTray::WebNotificationTray(WmShelf* shelf, |
| popup_alignment_delegate_->StartObserving(display::Screen::GetScreen(), |
| display); |
| OnMessageCenterTrayChanged(); |
| + |
| + tray_container()->set_insets( |
| + gfx::Insets(kTrayItemHorizontalInset, kTrayItemVerticalInset)); |
| } |
| WebNotificationTray::~WebNotificationTray() { |
| @@ -273,7 +365,6 @@ bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { |
| system_tray_->SetHideNotifications(true); |
| shelf()->UpdateAutoHideState(); |
| - button_->SetBubbleVisible(true); |
| SetDrawBackgroundAsActive(true); |
| return true; |
| } |
| @@ -291,7 +382,6 @@ void WebNotificationTray::HideMessageCenter() { |
| show_message_center_on_unlock_ = false; |
| system_tray_->SetHideNotifications(false); |
| shelf()->UpdateAutoHideState(); |
| - button_->SetBubbleVisible(false); |
| } |
| void WebNotificationTray::SetTrayBubbleHeight(int height) { |
| @@ -460,12 +550,6 @@ void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) { |
| message_center()->EnterQuietModeWithExpire(expires_in); |
| } |
| -void WebNotificationTray::ButtonPressed(views::Button* sender, |
| - const ui::Event& event) { |
| - DCHECK_EQ(button_, sender); |
| - PerformAction(event); |
| -} |
| - |
| void WebNotificationTray::OnMessageCenterTrayChanged() { |
| // Do not update the tray contents directly. Multiple change events can happen |
| // consecutively, and calling Update in the middle of those events will show |
| @@ -481,15 +565,60 @@ void WebNotificationTray::UpdateTrayContent() { |
| return; |
| should_update_tray_content_ = false; |
| + std::set<std::string> notification_ids; |
|
oshima
2016/08/04 18:15:21
unorderd_set or you can just use vector (which is
yoshiki
2016/08/05 13:30:48
Let me use unordered_map, since I want to show tha
|
| + for (auto i : visible_small_icons_) { |
| + notification_ids.insert(i.first); |
| + } |
| + |
| + // Add small icons (up to kMaximumSmallIconCount = 3). |
| message_center::MessageCenter* message_center = |
| message_center_tray_->message_center(); |
| - button_->SetUnreadCount(message_center->UnreadNotificationCount()); |
| - if (IsMessageCenterBubbleVisible()) |
| - button_->SetState(views::CustomButton::STATE_PRESSED); |
| - else |
| - button_->SetState(views::CustomButton::STATE_NORMAL); |
| + size_t visible_small_icon_count = 0; |
| + for (const auto* notification : message_center->GetVisibleNotifications()) { |
| + gfx::Image image = notification->small_image(); |
| + if (image.IsEmpty()) |
| + continue; |
| + |
| + if (visible_small_icon_count >= kMaximumSmallIconCount) |
| + break; |
| + visible_small_icon_count++; |
| + |
| + notification_ids.erase(notification->id()); |
| + if (visible_small_icons_.count(notification->id()) != 0) |
| + continue; |
| + |
| + auto* item = |
| + new WebNotificationImage(image.AsImageSkia(), kTrayItemInnerIconSize, |
| + animation_container_.get(), this); |
| + visible_small_icons_.insert(std::make_pair(notification->id(), item)); |
| + |
| + tray_container()->AddChildViewAt(item, 0); |
| + item->SetVisible(true); |
| + } |
| + |
| + // Remove unnecessary icons. |
| + for (const std::string& id : notification_ids) { |
| + WebNotificationImage* item = visible_small_icons_[id]; |
| + visible_small_icons_.erase(id); |
| + item->HideAndDelete(); |
| + } |
| + |
| + // Show or hide the bell icon. |
| + size_t visible_notification_count = message_center->NotificationCount(); |
| + bell_icon_->SetVisible(visible_notification_count == 0); |
| + |
| + // Show or hide the counter. |
| + size_t hidden_icon_count = |
| + visible_notification_count - visible_small_icon_count; |
| + if (hidden_icon_count != 0) { |
| + counter_->SetVisible(true); |
| + counter_->SetUnreadCount(hidden_icon_count); |
| + } else { |
| + counter_->SetVisible(false); |
| + } |
| SetVisible(IsLoggedIn()); |
| + PreferredSizeChanged(); |
| Layout(); |
| SchedulePaint(); |
| if (IsLoggedIn()) |