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()) |