Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(919)

Unified Diff: ash/system/tray/system_tray.cc

Issue 10383045: Add support to SystemTray for a second notification bubble. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ash/system/tray/system_tray.h ('k') | ash/system/tray/system_tray_item.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ash/system/tray/system_tray.cc
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 836066745f4e00377d166c572a179a4c0a6f5c10..2a94c3e160a531140b62b5e69f54cf9cc89ec0ee 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -210,10 +210,16 @@ class SystemTrayBubbleBackground : public views::Background {
class SystemTrayBubbleBorder : public views::BubbleBorder {
public:
- explicit SystemTrayBubbleBorder(views::View* owner)
+ enum ArrowType {
+ ARROW_TYPE_NONE,
+ ARROW_TYPE_BOTTOM,
+ };
+
+ SystemTrayBubbleBorder(views::View* owner, ArrowType arrow_type)
: views::BubbleBorder(views::BubbleBorder::BOTTOM_RIGHT,
views::BubbleBorder::NO_SHADOW),
- owner_(owner) {
+ owner_(owner),
+ arrow_type_(arrow_type) {
set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
}
@@ -256,30 +262,33 @@ class SystemTrayBubbleBorder : public views::BubbleBorder {
return;
// Draw the arrow.
- int left_base_x = base::i18n::IsRTL() ? kArrowWidth :
- owner_->width() - kArrowPaddingFromRight - kArrowWidth;
- int left_base_y = y;
- int tip_x = left_base_x + kArrowWidth / 2;
- int tip_y = left_base_y + kArrowHeight;
- SkPath path;
- path.incReserve(4);
- path.moveTo(SkIntToScalar(left_base_x), SkIntToScalar(left_base_y));
- path.lineTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y));
- path.lineTo(SkIntToScalar(left_base_x + kArrowWidth),
- SkIntToScalar(left_base_y));
-
- SkPaint paint;
- paint.setStyle(SkPaint::kFill_Style);
- paint.setColor(kBackgroundColor);
- canvas->DrawPath(path, paint);
-
- // Now draw the arrow border.
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setColor(kBorderDarkColor);
- canvas->DrawPath(path, paint);
+ if (arrow_type_ == ARROW_TYPE_BOTTOM) {
+ int left_base_x = base::i18n::IsRTL() ? kArrowWidth :
+ owner_->width() - kArrowPaddingFromRight - kArrowWidth;
+ int left_base_y = y;
+ int tip_x = left_base_x + kArrowWidth / 2;
+ int tip_y = left_base_y + kArrowHeight;
+ SkPath path;
+ path.incReserve(4);
+ path.moveTo(SkIntToScalar(left_base_x), SkIntToScalar(left_base_y));
+ path.lineTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y));
+ path.lineTo(SkIntToScalar(left_base_x + kArrowWidth),
+ SkIntToScalar(left_base_y));
+
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(kBackgroundColor);
+ canvas->DrawPath(path, paint);
+
+ // Now draw the arrow border.
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(kBorderDarkColor);
+ canvas->DrawPath(path, paint);
+ }
}
views::View* owner_;
+ ArrowType arrow_type_;
DISALLOW_COPY_AND_ASSIGN(SystemTrayBubbleBorder);
};
@@ -327,6 +336,11 @@ class SystemTrayBubbleView : public views::BubbleDelegateView {
GetBubbleFrameView()->SetBubbleBorder(border);
}
+ void UpdateAnchor() {
+ SizeToContents();
+ GetWidget()->GetRootView()->SchedulePaint();
+ }
+
// Called when the host is destroyed.
void reset_host() { host_ = NULL; }
@@ -351,21 +365,35 @@ class SystemTrayBubbleView : public views::BubbleDelegateView {
class SystemTrayBubble : public base::MessagePumpObserver,
public views::Widget::Observer {
public:
+ enum BubbleType {
+ BUBBLE_TYPE_DEFAULT,
+ BUBBLE_TYPE_DETAILED,
+ BUBBLE_TYPE_NOTIFICATION
+ };
+
+ enum AnchorType {
+ ANCHOR_TYPE_TRAY,
+ ANCHOR_TYPE_BUBBLE
+ };
+
SystemTrayBubble(ash::SystemTray* tray,
const std::vector<ash::SystemTrayItem*>& items,
- bool detailed);
+ BubbleType bubble_type);
virtual ~SystemTrayBubble();
// Creates |bubble_view_| and a child views for each member of |items_|.
// Also creates |bubble_widget_| and sets up animations.
void InitView(views::View* anchor,
+ AnchorType anchor_type,
bool can_activate,
ash::user::LoginStatus login_status);
- bool detailed() const { return detailed_; }
+ gfx::Rect GetAnchorRect() const;
+
+ BubbleType bubble_type() const { return bubble_type_; }
+ SystemTrayBubbleView* bubble_view() const { return bubble_view_; }
void DestroyItemViews();
- views::Widget* GetTrayWidget() const;
void StartAutoCloseTimer(int seconds);
void StopAutoCloseTimer();
void RestartAutoCloseTimer();
@@ -385,7 +413,9 @@ class SystemTrayBubble : public base::MessagePumpObserver,
SystemTrayBubbleView* bubble_view_;
views::Widget* bubble_widget_;
std::vector<ash::SystemTrayItem*> items_;
- bool detailed_;
+ BubbleType bubble_type_;
+ AnchorType anchor_type_;
+ views::View* anchor_;
int autoclose_delay_;
base::OneShotTimer<SystemTrayBubble> autoclose_;
@@ -419,22 +449,17 @@ void SystemTrayBubbleView::Init() {
}
gfx::Rect SystemTrayBubbleView::GetAnchorRect() {
- if (host_) {
- views::Widget* widget = host_->GetTrayWidget();
- if (widget->IsVisible()) {
- gfx::Rect rect = widget->GetWindowScreenBounds();
- rect.Inset(
- base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreen : 0, 0,
- base::i18n::IsRTL() ? 0 : kPaddingFromRightEdgeOfScreen,
- kPaddingFromBottomOfScreen);
- return rect;
- }
+ gfx::Rect rect;
+ if (host_)
+ rect = host_->GetAnchorRect();
+ if (rect.IsEmpty()) {
+ rect = gfx::Screen::GetPrimaryMonitor().bounds();
+ rect = gfx::Rect(base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreen :
+ rect.width() - kPaddingFromRightEdgeOfScreen,
+ rect.height() - kPaddingFromBottomOfScreen,
+ 0, 0);
}
- gfx::Rect rect = gfx::Screen::GetPrimaryMonitor().bounds();
- return gfx::Rect(base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreen :
- rect.width() - kPaddingFromRightEdgeOfScreen,
- rect.height() - kPaddingFromBottomOfScreen,
- 0, 0);
+ return rect;
}
void SystemTrayBubbleView::ChildPreferredSizeChanged(View* child) {
@@ -473,12 +498,14 @@ void SystemTrayBubbleView::OnMouseExited(const views::MouseEvent& event) {
SystemTrayBubble::SystemTrayBubble(
ash::SystemTray* tray,
const std::vector<ash::SystemTrayItem*>& items,
- bool detailed)
+ BubbleType bubble_type)
: tray_(tray),
bubble_view_(NULL),
bubble_widget_(NULL),
items_(items),
- detailed_(detailed),
+ bubble_type_(bubble_type),
+ anchor_type_(ANCHOR_TYPE_TRAY),
+ anchor_(NULL),
autoclose_delay_(0) {
}
@@ -499,17 +526,29 @@ SystemTrayBubble::~SystemTrayBubble() {
}
void SystemTrayBubble::InitView(views::View* anchor,
+ AnchorType anchor_type,
bool can_activate,
ash::user::LoginStatus login_status) {
DCHECK(bubble_view_ == NULL);
+ anchor_type_ = anchor_type;
+ anchor_ = anchor;
bubble_view_ = new SystemTrayBubbleView(anchor, this, can_activate);
for (std::vector<ash::SystemTrayItem*>::iterator it = items_.begin();
it != items_.end();
++it) {
- views::View* view = detailed_ ?
- (*it)->CreateDetailedView(login_status) :
- (*it)->CreateDefaultView(login_status);
+ views::View* view = NULL;
+ switch (bubble_type_) {
+ case BUBBLE_TYPE_DEFAULT:
+ view = (*it)->CreateDefaultView(login_status);
+ break;
+ case BUBBLE_TYPE_DETAILED:
+ view = (*it)->CreateDetailedView(login_status);
+ break;
+ case BUBBLE_TYPE_NOTIFICATION:
+ view = (*it)->CreateNotificationView(login_status);
+ break;
+ }
if (view)
bubble_view_->AddChildView(new TrayPopupItemContainer(view));
}
@@ -520,7 +559,13 @@ void SystemTrayBubble::InitView(views::View* anchor,
// Must occur after call to CreateBubble()
bubble_view_->SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
bubble_widget_->non_client_view()->frame_view()->set_background(NULL);
- bubble_view_->SetBubbleBorder(new SystemTrayBubbleBorder(bubble_view_));
+ SystemTrayBubbleBorder::ArrowType arrow_type;
+ if (anchor_type_ == ANCHOR_TYPE_TRAY)
+ arrow_type = SystemTrayBubbleBorder::ARROW_TYPE_BOTTOM;
+ else
+ arrow_type = SystemTrayBubbleBorder::ARROW_TYPE_NONE;
+ bubble_view_->SetBubbleBorder(
+ new SystemTrayBubbleBorder(bubble_view_, arrow_type));
bubble_widget_->AddObserver(this);
@@ -538,21 +583,46 @@ void SystemTrayBubble::InitView(views::View* anchor,
bubble_view_->Show();
}
+gfx::Rect SystemTrayBubble::GetAnchorRect() const {
+ gfx::Rect rect;
+ views::Widget* widget = anchor_->GetWidget();
sadrul 2012/05/08 19:40:17 I think instead of keeping anchor_ around, you can
stevenjb 2012/05/08 22:49:20 Good call. Done.
+ if (widget->IsVisible()) {
+ rect = widget->GetWindowScreenBounds();
+ if (anchor_type_ == ANCHOR_TYPE_TRAY) {
+ rect.Inset(
+ base::i18n::IsRTL() ? kPaddingFromRightEdgeOfScreen : 0,
+ 0,
+ base::i18n::IsRTL() ? 0 : kPaddingFromRightEdgeOfScreen,
+ kPaddingFromBottomOfScreen);
+ } else if (anchor_type_ == ANCHOR_TYPE_BUBBLE) {
+ rect.Inset(
+ base::i18n::IsRTL() ? kShadowThickness - 1 : 0,
+ 0,
+ base::i18n::IsRTL() ? 0 : kShadowThickness - 1,
+ 0);
+ }
+ }
+ return rect;
+}
+
void SystemTrayBubble::DestroyItemViews() {
for (std::vector<ash::SystemTrayItem*>::iterator it = items_.begin();
it != items_.end();
++it) {
- if (detailed_)
- (*it)->DestroyDetailedView();
- else
- (*it)->DestroyDefaultView();
+ switch (bubble_type_) {
+ case BUBBLE_TYPE_DEFAULT:
+ (*it)->DestroyDefaultView();
+ break;
+ case BUBBLE_TYPE_DETAILED:
+ (*it)->DestroyDetailedView();
+ break;
+ case BUBBLE_TYPE_NOTIFICATION:
+ (*it)->DestroyNotificationView();
+ break;
+ }
}
}
-views::Widget* SystemTrayBubble::GetTrayWidget() const {
- return tray_->GetWidget();
-}
-
void SystemTrayBubble::StartAutoCloseTimer(int seconds) {
autoclose_.Stop();
autoclose_delay_ = seconds;
@@ -580,7 +650,8 @@ void SystemTrayBubble::Close() {
base::EventStatus SystemTrayBubble::WillProcessEvent(
const base::NativeEvent& event) {
// Check if the user clicked outside of the bubble and close it if they did.
- if (ui::EventTypeFromNative(event) == ui::ET_MOUSE_PRESSED) {
+ if (bubble_type_ != BUBBLE_TYPE_NOTIFICATION &&
+ ui::EventTypeFromNative(event) == ui::ET_MOUSE_PRESSED) {
gfx::Point cursor_in_view = ui::EventLocationFromNative(event);
views::View::ConvertPointFromScreen(bubble_view_, &cursor_in_view);
if (!bubble_view_->HitTest(cursor_in_view)) {
@@ -608,10 +679,36 @@ void SystemTrayBubble::OnWidgetVisibilityChanged(views::Widget* widget,
MessageLoopForUI::current()->AddObserver(this);
}
+// Observe the tray layer animation and update the anchor when it changes.
+// TODO(stevenjb): Observe or mirror the actual animation, not just the start
+// and end points.
+class SystemTrayLayerAnimationObserver : public ui::LayerAnimationObserver {
+ public:
+ explicit SystemTrayLayerAnimationObserver(SystemTray* host) : host_(host) {}
+
+ virtual void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) {
+ host_->UpdateNotificationAnchor();
+ }
+
+ virtual void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) {
+ host_->UpdateNotificationAnchor();
+ }
+
+ virtual void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) {
+ host_->UpdateNotificationAnchor();
+ }
+
+ private:
+ SystemTray* host_;
sadrul 2012/05/08 19:40:17 DISALLOW_COPY_AND_ASSIGN
stevenjb 2012/05/08 22:49:20 Done.
+};
+
} // namespace internal
// SystemTray
+using internal::SystemTrayBubble;
+using internal::SystemTrayLayerAnimationObserver;
+
SystemTray::SystemTray()
: items_(),
accessibility_observer_(NULL),
@@ -633,17 +730,17 @@ SystemTray::SystemTray()
0, kTrayBackgroundAlpha)),
ALLOW_THIS_IN_INITIALIZER_LIST(hover_background_animator_(this,
0, kTrayBackgroundHoverAlpha - kTrayBackgroundAlpha)) {
- container_ = new views::View;
- container_->SetLayoutManager(new views::BoxLayout(
+ tray_container_ = new views::View;
+ tray_container_->SetLayoutManager(new views::BoxLayout(
views::BoxLayout::kHorizontal, 0, 0, 0));
- container_->set_background(background_);
- container_->set_border(
+ tray_container_->set_background(background_);
+ tray_container_->set_border(
views::Border::CreateEmptyBorder(1, 1, 1, 1));
set_border(views::Border::CreateEmptyBorder(0, 0,
kPaddingFromBottomOfScreen, kPaddingFromRightEdgeOfScreen));
set_notify_enter_exit_on_child(true);
SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
- AddChildView(container_);
+ AddChildView(tray_container_);
// Initially we want to paint the background, but without the hover effect.
SetPaintsBackground(true, internal::BackgroundAnimator::CHANGE_IMMEDIATE);
@@ -725,6 +822,10 @@ void SystemTray::CreateWidget() {
widget_->SetContentsView(status_area_view);
widget_->Show();
widget_->GetNativeView()->SetName("StatusTrayWidget");
+
+ layer_animation_observer_.reset(new SystemTrayLayerAnimationObserver(this));
+ widget_->GetNativeView()->layer()->GetAnimator()->AddObserver(
+ layer_animation_observer_.get());
}
void SystemTray::AddTrayItem(SystemTrayItem* item) {
@@ -733,7 +834,7 @@ void SystemTray::AddTrayItem(SystemTrayItem* item) {
SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate();
views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus());
if (tray_item) {
- container_->AddChildViewAt(tray_item, 0);
+ tray_container_->AddChildViewAt(tray_item, 0);
PreferredSizeChanged();
}
}
@@ -755,8 +856,26 @@ void SystemTray::ShowDetailedView(SystemTrayItem* item,
bubble_->StartAutoCloseTimer(close_delay);
}
+void SystemTray::ShowNotificationView(SystemTrayItem* item) {
+ if (std::find(notification_items_.begin(), notification_items_.end(), item)
+ != notification_items_.end())
+ return;
+ notification_items_.push_back(item);
+ ShowNotifications();
+}
+
+void SystemTray::HideNotificationView(SystemTrayItem* item) {
+ std::vector<SystemTrayItem*>::iterator found_iter =
+ std::find(notification_items_.begin(), notification_items_.end(), item);
+ if (found_iter == notification_items_.end())
+ return;
+ notification_items_.erase(found_iter);
+ ShowNotifications();
+}
+
void SystemTray::SetDetailedViewCloseDelay(int close_delay) {
- if (bubble_.get() && bubble_->detailed())
+ if (bubble_.get() &&
+ bubble_->bubble_type() == SystemTrayBubble::BUBBLE_TYPE_DETAILED)
bubble_->StartAutoCloseTimer(close_delay);
}
@@ -782,18 +901,23 @@ bool SystemTray::CloseBubbleForTest() const {
// Private methods.
-void SystemTray::RemoveBubble(internal::SystemTrayBubble* bubble) {
- CHECK_EQ(bubble_.get(), bubble);
- bubble_.reset();
-
- if (should_show_launcher_) {
- // No need to show the launcher if the mouse isn't over the status area
- // anymore.
- aura::RootWindow* root = GetWidget()->GetNativeView()->GetRootWindow();
- should_show_launcher_ = GetWidget()->GetWindowScreenBounds().Contains(
- root->last_mouse_location());
- if (!should_show_launcher_)
- Shell::GetInstance()->shelf()->UpdateAutoHideState();
+void SystemTray::RemoveBubble(SystemTrayBubble* bubble) {
+ if (bubble == bubble_.get()) {
+ bubble_.reset();
+ ShowNotifications(); // State changed, re-create notifications.
+ if (should_show_launcher_) {
+ // No need to show the launcher if the mouse isn't over the status area
+ // anymore.
+ aura::RootWindow* root = GetWidget()->GetNativeView()->GetRootWindow();
+ should_show_launcher_ = GetWidget()->GetWindowScreenBounds().Contains(
+ root->last_mouse_location());
+ if (!should_show_launcher_)
+ Shell::GetInstance()->shelf()->UpdateAutoHideState();
+ }
+ } else if (bubble == notification_bubble_) {
+ notification_bubble_.reset();
+ } else {
+ NOTREACHED();
}
}
@@ -807,23 +931,66 @@ void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items,
bool detailed,
bool can_activate) {
// Destroy any existing bubble and create a new one.
- bubble_.reset(new internal::SystemTrayBubble(this, items, detailed));
+ SystemTrayBubble::BubbleType bubble_type = detailed ?
+ SystemTrayBubble::BUBBLE_TYPE_DETAILED :
+ SystemTrayBubble::BUBBLE_TYPE_DEFAULT;
+ bubble_.reset(new SystemTrayBubble(this, items, bubble_type));
ash::SystemTrayDelegate* delegate =
ash::Shell::GetInstance()->tray_delegate();
- bubble_->InitView(container_, can_activate, delegate->GetUserLoginStatus());
+ views::View* anchor = tray_container_;
+ bubble_->InitView(anchor, SystemTrayBubble::ANCHOR_TYPE_TRAY,
+ can_activate, delegate->GetUserLoginStatus());
// If we have focus the shelf should be visible and we need to continue
// showing the shelf when the popup is shown.
if (GetWidget()->IsActive())
should_show_launcher_ = true;
+ ShowNotifications(); // State changed, re-create notifications.
+}
+
+void SystemTray::ShowNotifications() {
sadrul 2012/05/08 19:40:17 Since this can hide the notifications in some case
stevenjb 2012/05/08 22:49:20 Done.
+ // Only show the notification buble if we have notifications and we are not
+ // showing the default bubble.
+ if (notification_items_.empty() ||
+ (bubble_.get() &&
+ bubble_->bubble_type() == SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) {
+ notification_bubble_.reset();
+ return;
+ }
+ notification_bubble_.reset(
+ new SystemTrayBubble(this, notification_items_,
+ SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION));
+ views::View* anchor;
+ SystemTrayBubble::AnchorType anchor_type;
+ if (bubble_.get()) {
+ anchor = bubble_->bubble_view();
+ anchor_type = SystemTrayBubble::ANCHOR_TYPE_BUBBLE;
+ } else {
+ anchor = tray_container_;
+ anchor_type = SystemTrayBubble::ANCHOR_TYPE_TRAY;
+ }
+ notification_bubble_->InitView(
+ anchor, anchor_type,
+ false /* can_activate */,
+ ash::Shell::GetInstance()->tray_delegate()->GetUserLoginStatus());
+}
+
+void SystemTray::UpdateNotificationAnchor() {
+ if (!notification_bubble_.get())
+ return;
+ notification_bubble_->bubble_view()->UpdateAnchor();
+ // Ensure that the notification buble is above the launcher/status area.
+ notification_bubble_->bubble_view()->GetWidget()->StackAtTop();
}
bool SystemTray::PerformAction(const views::Event& event) {
// If we're already showing the default view, hide it; otherwise, show it
// (and hide any popup that's currently shown).
- if (bubble_.get() && !bubble_->detailed())
+ if (bubble_.get() &&
+ bubble_->bubble_type() == SystemTrayBubble::BUBBLE_TYPE_DEFAULT) {
bubble_->Close();
- else
+ } else {
ShowDefaultView();
+ }
return true;
}
@@ -858,7 +1025,7 @@ void SystemTray::OnPaintFocusBorder(gfx::Canvas* canvas) {
// sure clicking on the edges brings up the popup. However, the focus border
// should be only around the container.
if (GetWidget() && GetWidget()->IsActive())
- canvas->DrawFocusRect(container_->bounds());
+ canvas->DrawFocusRect(tray_container_->bounds());
}
void SystemTray::UpdateBackground(int alpha) {
« no previous file with comments | « ash/system/tray/system_tray.h ('k') | ash/system/tray/system_tray_item.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698