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..98fd7d2c83089937c4cfdd76d267c850dc6a7bc8 100644 |
--- a/ash/common/system/web_notification/web_notification_tray.cc |
+++ b/ash/common/system/web_notification/web_notification_tray.cc |
@@ -63,8 +63,20 @@ 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(16, 16); |
+constexpr gfx::Size kTrayItemInnerBellIconSize(18, 18); |
+constexpr gfx::Size kTrayItemOuterSize(26, 26); |
+constexpr gfx::Insets kTrayItemInsets(6, 6); |
+ |
+constexpr int kTrayItemAnimationDurationMS = 200; |
+ |
+// Flag to disable animation. Only for testing. |
+static bool disable_animations_for_test = false; |
} |
namespace { |
@@ -115,92 +127,162 @@ 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); |
- |
- gfx::ImageSkia image; |
- if (MaterialDesignController::IsShelfMaterial()) { |
- image = CreateVectorIcon(gfx::VectorIconId::SHELF_NOTIFICATIONS, |
- kShelfIconColor); |
- } else { |
- image = |
- CreateVectorIcon(gfx::VectorIconId::NOTIFICATIONS, kNoUnreadIconSize, |
- kWebNotificationColorNoUnread); |
- } |
+ WebNotificationItem(gfx::AnimationContainer* container, |
+ WebNotificationTray* tray) |
+ : tray_(tray) { |
+ SetPaintToLayer(true); |
+ layer()->SetFillsBoundsOpaquely(false); |
+ views::View::SetVisible(false); |
+ set_owned_by_client(); |
- 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() || disable_animations_for_test) { |
+ 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); |
+ if (!animation_.get() || !animation_->is_animating()) |
+ return kTrayItemOuterSize; |
+ |
+ // Animate the width (or height) when this item shows (or hides). |
+ // Note that other tray items do the same effect. |
oshima
2016/08/05 15:08:08
This does not explain why you want to change the s
yoshiki
2016/08/05 16:15:38
Yes, I'd like to shift the left buttons along with
|
+ gfx::Size size = kTrayItemOuterSize; |
+ if (IsHorizontalLayout()) { |
+ size.set_width(std::max( |
+ 1, gfx::ToRoundedInt(size.width() * animation_->GetCurrentValue()))); |
+ } else { |
+ size.set_height(std::max( |
+ 1, gfx::ToRoundedInt(size.height() * animation_->GetCurrentValue()))); |
+ } |
+ return size; |
} |
int GetHeightForWidth(int width) const override { |
return GetPreferredSize().height(); |
} |
+ bool IsHorizontalLayout() const { |
+ return IsHorizontalAlignment(tray_->shelf_alignment()); |
+ } |
+ |
private: |
- void UpdateIconVisibility() { |
- if (unread_count_ == 0) { |
- if (!Contains(&no_unread_icon_)) { |
- RemoveAllChildViews(false /* delete_children */); |
- AddChildView(&no_unread_icon_); |
- } |
+ // gfx::AnimationDelegate: |
+ void AnimationProgressed(const gfx::Animation* animation) override { |
+ gfx::Transform transform; |
+ if (IsHorizontalLayout()) { |
+ transform.Translate(0, animation->CurrentValueBetween( |
+ static_cast<double>(height()) / 2, 0.)); |
oshima
2016/08/05 15:08:08
height() / 2.f
yoshiki
2016/08/05 16:15:38
Let me use "2." for double since it should be doub
|
} 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.), |
oshima
2016/08/05 15:08:08
ditto
yoshiki
2016/08/05 16:15:38
Done.
|
+ 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); |
} |
- bool is_bubble_visible_; |
- int unread_count_; |
+ std::unique_ptr<gfx::SlideAnimation> animation_; |
+ bool delete_after_animation_ = false; |
+ WebNotificationTray* tray_; |
- views::ImageView no_unread_icon_; |
- views::Label unread_label_; |
+ 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_); |
+ } |
- DISALLOW_COPY_AND_ASSIGN(WebNotificationButton); |
+ private: |
+ views::ImageView* view_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationImage); |
+}; |
+ |
+class WebNotificationLabel : public WebNotificationItem { |
+ public: |
+ WebNotificationLabel(gfx::AnimationContainer* container, |
+ WebNotificationTray* tray) |
+ : WebNotificationItem(container, tray) { |
+ view_ = new views::Label(); |
+ SetupLabelForTray(view_); |
+ } |
+ |
+ void SetNotificationCount(bool small_icons_exist, size_t notification_count) { |
+ if (notification_count > 99) |
+ notification_count = 99; // cap with 99. |
oshima
2016/08/05 15:08:08
std::min ?
yoshiki
2016/08/05 16:15:38
Done.
|
+ |
+ base::string16 str = base::FormatNumber(notification_count); |
+ if (small_icons_exist) |
+ str = base::ASCIIToUTF16("+") + str; |
oshima
2016/08/05 15:08:08
Does this work in RTL?
yoshiki
2016/08/05 16:15:38
Done.
|
+ |
+ view_->SetText(str); |
+ view_->SetEnabledColor(kWebNotificationColorWithUnread); |
+ AddChildView(view_); |
+ SchedulePaint(); |
+ } |
+ |
+ private: |
+ views::Label* view_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WebNotificationLabel); |
}; |
WebNotificationTray::WebNotificationTray(WmShelf* shelf, |
@@ -209,18 +291,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 +328,8 @@ WebNotificationTray::WebNotificationTray(WmShelf* shelf, |
popup_alignment_delegate_->StartObserving(display::Screen::GetScreen(), |
display); |
OnMessageCenterTrayChanged(); |
+ |
+ tray_container()->set_insets(kTrayItemInsets); |
} |
WebNotificationTray::~WebNotificationTray() { |
@@ -243,6 +339,11 @@ WebNotificationTray::~WebNotificationTray() { |
popup_collection_.reset(); |
} |
+// static |
+void WebNotificationTray::DisableAnimationsForTest() { |
+ disable_animations_for_test = true; |
oshima
2016/08/05 15:08:08
Once one test set this, this will be used for all
yoshiki
2016/08/05 16:15:38
Fixed. TrayItemView does same thing so I thought i
|
+} |
+ |
// Public methods. |
bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { |
@@ -255,7 +356,7 @@ bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { |
message_center_tray_.get(), true); |
int max_height; |
- if (IsHorizontalAlignment(shelf()->GetAlignment())) { |
+ if (IsHorizontalAlignment(shelf_alignment())) { |
max_height = shelf()->GetIdealBounds().y(); |
} else { |
// Assume the status area and bubble bottoms are aligned when vertical. |
@@ -273,7 +374,6 @@ bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { |
system_tray_->SetHideNotifications(true); |
shelf()->UpdateAutoHideState(); |
- button_->SetBubbleVisible(true); |
SetDrawBackgroundAsActive(true); |
return true; |
} |
@@ -291,7 +391,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 +559,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 +574,62 @@ void WebNotificationTray::UpdateTrayContent() { |
return; |
should_update_tray_content_ = false; |
+ std::unordered_set<std::string> notification_ids; |
+ for (auto i : visible_small_icons_) { |
+ notification_ids.insert(i.first); |
+ } |
oshima
2016/08/05 15:08:08
nit: nuke {}
yoshiki
2016/08/05 16:15:37
Done.
|
+ |
+ // 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_->SetNotificationCount( |
+ (visible_small_icon_count != 0), // small_icons_exist |
+ hidden_icon_count); |
+ } else { |
+ counter_->SetVisible(false); |
+ } |
SetVisible(IsLoggedIn()); |
+ PreferredSizeChanged(); |
Layout(); |
SchedulePaint(); |
if (IsLoggedIn()) |