Commit a5a068d6 authored by mukai@chromium.org's avatar mukai@chromium.org

Separate the logic of popup alignment and workarea handling as delegate.

MessagePopupCollection contains plenty size of conditions and ifdefs
to work properly with each type of the desktop we have, and some
logic makes side effects on another desktop. This design is unhealty
and adding more conditions sounds incorrect.

Considering this, it would be better to extract platform dependent
parts as a delegate class (PopupAlignmentDelegate) and allow
subclasses to provide platform-specific features.

This design is also beneficial for win-ash, because we had
OS_CHROMEOS layout data and logic which actually means Ash.

BUG=389656
R=stevenjb@chromium.org, dimich@chromium.org
TBR=jamescook@chromium.org, harrym@chromium.org
TEST=message_center_unittests, ash_unittests, some manual checks

Review URL: https://codereview.chromium.org/369573004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282269 0039d316-1c4b-4281-b951-d872f2087c98
parent 9bd9fc06
......@@ -504,6 +504,8 @@
'system/user/user_card_view.h',
'system/user/user_view.cc',
'system/user/user_view.h',
'system/web_notification/ash_popup_alignment_delegate.cc',
'system/web_notification/ash_popup_alignment_delegate.h',
'system/web_notification/web_notification_tray.cc',
'system/web_notification/web_notification_tray.h',
'system/win/audio/tray_audio_delegate_win.cc',
......@@ -987,6 +989,7 @@
'system/tray/system_tray_unittest.cc',
'system/tray/tray_details_view_unittest.cc',
'system/user/tray_user_unittest.cc',
'system/web_notification/ash_popup_alignment_delegate_unittest.cc',
'system/web_notification/web_notification_tray_unittest.cc',
'test/ash_test_helper_unittest.cc',
'test/ash_unittests.cc',
......
......@@ -47,6 +47,7 @@ class ShelfLayoutManagerTest;
class ShelfWidget;
class StatusAreaWidget;
class WorkspaceController;
FORWARD_DECLARE_TEST(AshPopupAlignmentDelegateTest, AutoHide);
FORWARD_DECLARE_TEST(WebNotificationTrayTest, PopupAndFullscreen);
// ShelfLayoutManager is the layout manager responsible for the shelf and
......@@ -226,6 +227,7 @@ class ASH_EXPORT ShelfLayoutManager :
friend class ash::ScreenAsh;
friend class PanelLayoutManagerTest;
friend class ShelfLayoutManagerTest;
FRIEND_TEST_ALL_PREFIXES(ash::AshPopupAlignmentDelegateTest, AutoHide);
FRIEND_TEST_ALL_PREFIXES(ash::WebNotificationTrayTest, PopupAndFullscreen);
struct TargetBounds {
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
#include "ash/display/display_controller.h"
#include "ash/shelf/shelf_constants.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_types.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "base/i18n/rtl.h"
#include "ui/aura/window.h"
#include "ui/gfx/display.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/screen.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/message_popup_collection.h"
namespace ash {
namespace {
const int kToastMarginX = 3;
// If there should be no margin for the first item, this value needs to be
// substracted to flush the message to the shelf (the width of the border +
// shadow).
const int kNoToastMarginBorderAndShadowOffset = 2;
}
AshPopupAlignmentDelegate::AshPopupAlignmentDelegate()
: display_id_(gfx::Display::kInvalidDisplayID),
screen_(NULL),
root_window_(NULL),
shelf_(NULL),
system_tray_height_(0) {
}
AshPopupAlignmentDelegate::~AshPopupAlignmentDelegate() {
if (screen_)
screen_->RemoveObserver(this);
Shell::GetInstance()->RemoveShellObserver(this);
if (shelf_)
shelf_->RemoveObserver(this);
}
void AshPopupAlignmentDelegate::StartObserving(gfx::Screen* screen,
const gfx::Display& display) {
screen_ = screen;
display_id_ = display.id();
UpdateShelf();
screen->AddObserver(this);
Shell::GetInstance()->AddShellObserver(this);
if (system_tray_height_ > 0)
OnAutoHideStateChanged(shelf_->auto_hide_state());
}
void AshPopupAlignmentDelegate::SetSystemTrayHeight(int height) {
system_tray_height_ = height;
// If the shelf is shown during auto-hide state, the distance from the edge
// should be reduced by the height of shelf's shown height.
if (shelf_ && shelf_->visibility_state() == SHELF_AUTO_HIDE &&
shelf_->auto_hide_state() == SHELF_AUTO_HIDE_SHOWN) {
system_tray_height_ -= kShelfSize - ShelfLayoutManager::kAutoHideSize;
}
if (system_tray_height_ > 0)
system_tray_height_ += message_center::kMarginBetweenItems;
else
system_tray_height_ = 0;
if (!shelf_)
return;
DoUpdateIfPossible();
}
int AshPopupAlignmentDelegate::GetToastOriginX(
const gfx::Rect& toast_bounds) const {
// In Ash, RTL UI language mirrors the whole ash layout, so the toast
// widgets should be at the bottom-left instead of bottom right.
if (base::i18n::IsRTL())
return work_area_.x() + kToastMarginX;
if (IsFromLeft())
return work_area_.x() + kToastMarginX;
return work_area_.right() - kToastMarginX - toast_bounds.width();
}
int AshPopupAlignmentDelegate::GetBaseLine() const {
return IsTopDown()
? work_area_.y() + kNoToastMarginBorderAndShadowOffset +
system_tray_height_
: work_area_.bottom() - kNoToastMarginBorderAndShadowOffset -
system_tray_height_;
}
int AshPopupAlignmentDelegate::GetWorkAreaBottom() const {
return work_area_.bottom() - system_tray_height_;
}
bool AshPopupAlignmentDelegate::IsTopDown() const {
return GetAlignment() == SHELF_ALIGNMENT_TOP;
}
bool AshPopupAlignmentDelegate::IsFromLeft() const {
return GetAlignment() == SHELF_ALIGNMENT_LEFT;
}
void AshPopupAlignmentDelegate::RecomputeAlignment(
const gfx::Display& display) {
// Nothing needs to be done.
}
ShelfAlignment AshPopupAlignmentDelegate::GetAlignment() const {
return shelf_ ? shelf_->GetAlignment() : SHELF_ALIGNMENT_BOTTOM;
}
void AshPopupAlignmentDelegate::UpdateShelf() {
if (shelf_)
return;
aura::Window* root_window = ash::Shell::GetInstance()->display_controller()->
GetRootWindowForDisplayId(display_id_);
shelf_ = ShelfLayoutManager::ForShelf(root_window);
if (shelf_)
shelf_->AddObserver(this);
}
void AshPopupAlignmentDelegate::OnDisplayWorkAreaInsetsChanged() {
UpdateShelf();
work_area_ = Shell::GetScreen()->GetDisplayNearestWindow(
shelf_->shelf_widget()->GetNativeView()).work_area();
}
void AshPopupAlignmentDelegate::OnAutoHideStateChanged(
ShelfAutoHideState new_state) {
work_area_ = Shell::GetScreen()->GetDisplayNearestWindow(
shelf_->shelf_widget()->GetNativeView()).work_area();
int width = 0;
if ((shelf_->visibility_state() == SHELF_AUTO_HIDE) &&
new_state == SHELF_AUTO_HIDE_SHOWN) {
// Since the work_area is already reduced by kAutoHideSize, the inset width
// should be just the difference.
width = kShelfSize - ShelfLayoutManager::kAutoHideSize;
}
work_area_.Inset(shelf_->SelectValueForShelfAlignment(
gfx::Insets(0, 0, width, 0),
gfx::Insets(0, width, 0, 0),
gfx::Insets(0, 0, 0, width),
gfx::Insets(width, 0, 0, 0)));
DoUpdateIfPossible();
}
void AshPopupAlignmentDelegate::OnDisplayAdded(
const gfx::Display& new_display) {
}
void AshPopupAlignmentDelegate::OnDisplayRemoved(
const gfx::Display& old_display) {
}
void AshPopupAlignmentDelegate::OnDisplayMetricsChanged(
const gfx::Display& display,
uint32_t metrics) {
if (display.id() == display_id_ && shelf_)
OnAutoHideStateChanged(shelf_->auto_hide_state());
}
} // namespace ash
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_WEB_NOTIFICATION_ASH_POPUP_ALIGNMENT_DELEGATE_H_
#define ASH_SYSTEM_WEB_NOTIFICATION_ASH_POPUP_ALIGNMENT_DELEGATE_H_
#include "ash/ash_export.h"
#include "ash/shelf/shelf_layout_manager_observer.h"
#include "ash/shelf/shelf_types.h"
#include "ash/shell_observer.h"
#include "base/macros.h"
#include "ui/gfx/display_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/message_center/views/popup_alignment_delegate.h"
namespace aura {
class Window;
}
namespace gfx {
class Screen;
}
namespace ash {
class AshPopupAlignmentDelegateTest;
class ShelfLayoutManager;
class WebNotificationTrayTest;
// The PopupAlignmentDelegate subclass for Ash. It needs to handle alignment of
// the shelf and its autohide state.
class ASH_EXPORT AshPopupAlignmentDelegate
: public message_center::PopupAlignmentDelegate,
public ShelfLayoutManagerObserver,
public ShellObserver,
public gfx::DisplayObserver {
public:
AshPopupAlignmentDelegate();
virtual ~AshPopupAlignmentDelegate();
// Start observing the system.
void StartObserving(gfx::Screen* screen, const gfx::Display& display);
// Sets the current height of the system tray so that the notification toasts
// can avoid it.
void SetSystemTrayHeight(int height);
// Overridden from message_center::PopupAlignmentDelegate:
virtual int GetToastOriginX(const gfx::Rect& toast_bounds) const OVERRIDE;
virtual int GetBaseLine() const OVERRIDE;
virtual int GetWorkAreaBottom() const OVERRIDE;
virtual bool IsTopDown() const OVERRIDE;
virtual bool IsFromLeft() const OVERRIDE;
virtual void RecomputeAlignment(const gfx::Display& display) OVERRIDE;
private:
friend class AshPopupAlignmentDelegateTest;
friend class WebNotificationTrayTest;
// Get the current alignment of the shelf.
ShelfAlignment GetAlignment() const;
// Update |shelf_| and start watching when it's first set. This should not
// be done in the constructor because the shelf might not be initialized at
// that point.
void UpdateShelf();
// Overridden from ShellObserver:
virtual void OnDisplayWorkAreaInsetsChanged() OVERRIDE;
// Overridden from ShelfLayoutManagerObserver:
virtual void OnAutoHideStateChanged(ShelfAutoHideState new_state) OVERRIDE;
// Overridden from gfx::DisplayObserver:
virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
virtual void OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t metrics) OVERRIDE;
int64_t display_id_;
gfx::Screen* screen_;
gfx::Rect work_area_;
aura::Window* root_window_;
ShelfLayoutManager* shelf_;
int system_tray_height_;
DISALLOW_COPY_AND_ASSIGN(AshPopupAlignmentDelegate);
};
} // namespace ash
#endif // ASH_SYSTEM_WEB_NOTIFICATION_ASH_POPUP_ALIGNMENT_DELEGATE_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
#include <vector>
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_types.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
#include "ui/gfx/screen.h"
#include "ui/message_center/message_center_style.h"
namespace ash {
class AshPopupAlignmentDelegateTest : public test::AshTestBase {
public:
AshPopupAlignmentDelegateTest() {}
virtual ~AshPopupAlignmentDelegateTest() {}
virtual void SetUp() {
test::AshTestBase::SetUp();
alignment_delegate_.reset(new AshPopupAlignmentDelegate());
alignment_delegate_->StartObserving(
Shell::GetScreen(), Shell::GetScreen()->GetPrimaryDisplay());
}
virtual void TearDown() {
alignment_delegate_.reset();
test::AshTestBase::TearDown();
}
protected:
enum Position {
TOP_LEFT,
TOP_RIGHT,
BOTTOM_LEFT,
BOTTOM_RIGHT,
OUTSIDE
};
AshPopupAlignmentDelegate* alignment_delegate() {
return alignment_delegate_.get();
}
Position GetPositionInDisplay(const gfx::Point& point) {
const gfx::Rect& work_area =
Shell::GetScreen()->GetPrimaryDisplay().work_area();
const gfx::Point center_point = work_area.CenterPoint();
if (work_area.x() > point.x() || work_area.y() > point.y() ||
work_area.right() < point.x() || work_area.bottom() < point.y()) {
return OUTSIDE;
}
if (center_point.x() < point.x())
return (center_point.y() < point.y()) ? BOTTOM_RIGHT : TOP_RIGHT;
else
return (center_point.y() < point.y()) ? BOTTOM_LEFT : TOP_LEFT;
}
gfx::Rect GetWorkArea() {
return alignment_delegate_->work_area_;
}
private:
scoped_ptr<AshPopupAlignmentDelegate> alignment_delegate_;
};
TEST_F(AshPopupAlignmentDelegateTest, ShelfAlignment) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
gfx::Point toast_point;
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
Shell::GetInstance()->SetShelfAlignment(
SHELF_ALIGNMENT_RIGHT,
Shell::GetPrimaryRootWindow());
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
Shell::GetInstance()->SetShelfAlignment(
SHELF_ALIGNMENT_LEFT,
Shell::GetPrimaryRootWindow());
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_LEFT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_TRUE(alignment_delegate()->IsFromLeft());
Shell::GetInstance()->SetShelfAlignment(
SHELF_ALIGNMENT_TOP,
Shell::GetPrimaryRootWindow());
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(TOP_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_TRUE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
}
TEST_F(AshPopupAlignmentDelegateTest, LockScreen) {
const gfx::Rect toast_size(0, 0, 10, 10);
Shell::GetInstance()->SetShelfAlignment(
SHELF_ALIGNMENT_LEFT,
Shell::GetPrimaryRootWindow());
gfx::Point toast_point;
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_LEFT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_TRUE(alignment_delegate()->IsFromLeft());
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
toast_point.set_x(alignment_delegate()->GetToastOriginX(toast_size));
toast_point.set_y(alignment_delegate()->GetBaseLine());
EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
}
TEST_F(AshPopupAlignmentDelegateTest, AutoHide) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
// Create a window, otherwise autohide doesn't work.
scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
Shell::GetInstance()->SetShelfAutoHideBehavior(
SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS,
Shell::GetPrimaryRootWindow());
ShelfLayoutManager::ForShelf(Shell::GetPrimaryRootWindow())->
UpdateAutoHideStateNow();
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_LT(baseline, alignment_delegate()->GetBaseLine());
}
// Verify that docked window doesn't affect the popup alignment.
TEST_F(AshPopupAlignmentDelegateTest, DockedWindow) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
scoped_ptr<aura::Window> window(
CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 50, 50)));
aura::Window* docked_container = Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
kShellWindowId_DockedContainer);
docked_container->AddChild(window.get());
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_EQ(baseline, alignment_delegate()->GetBaseLine());
EXPECT_FALSE(alignment_delegate()->IsTopDown());
EXPECT_FALSE(alignment_delegate()->IsFromLeft());
}
TEST_F(AshPopupAlignmentDelegateTest, DisplayResize) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
UpdateDisplay("800x800");
EXPECT_LT(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_LT(baseline, alignment_delegate()->GetBaseLine());
UpdateDisplay("400x400");
EXPECT_GT(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_GT(baseline, alignment_delegate()->GetBaseLine());
}
TEST_F(AshPopupAlignmentDelegateTest, TrayHeight) {
const gfx::Rect toast_size(0, 0, 10, 10);
UpdateDisplay("600x600");
int origin_x = alignment_delegate()->GetToastOriginX(toast_size);
int baseline = alignment_delegate()->GetBaseLine();
const int kTrayHeight = 100;
alignment_delegate()->SetSystemTrayHeight(kTrayHeight);
EXPECT_EQ(origin_x, alignment_delegate()->GetToastOriginX(toast_size));
EXPECT_EQ(baseline - kTrayHeight - message_center::kMarginBetweenItems,
alignment_delegate()->GetBaseLine());
}
} // namespace ash
......@@ -17,6 +17,7 @@
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_utils.h"
#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
#include "base/auto_reset.h"
#include "base/i18n/number_formatting.h"
#include "base/i18n/rtl.h"
......@@ -70,134 +71,6 @@ const SkColor kWebNotificationColorWithUnread = SK_ColorWHITE;
}
// Observes the change of work area (including temporary change by auto-hide)
// and notifies MessagePopupCollection.
class WorkAreaObserver : public ShelfLayoutManagerObserver,
public ShellObserver {
public:
WorkAreaObserver();
virtual ~WorkAreaObserver();
void SetSystemTrayHeight(int height);
// Starts observing |shelf| and shell and sends the change to |collection|.
void StartObserving(message_center::MessagePopupCollection* collection,
aura::Window* root_window);
// Stops the observing session.
void StopObserving();
// Overridden from ShellObserver:
virtual void OnDisplayWorkAreaInsetsChanged() OVERRIDE;
// Overridden from ShelfLayoutManagerObserver:
virtual void OnAutoHideStateChanged(ShelfAutoHideState new_state) OVERRIDE;
private:
// Updates |shelf_| from |root_window_|.
void UpdateShelf();
message_center::MessagePopupCollection* collection_;
aura::Window* root_window_;
ShelfLayoutManager* shelf_;
int system_tray_height_;
DISALLOW_COPY_AND_ASSIGN(WorkAreaObserver);
};
WorkAreaObserver::WorkAreaObserver()
: collection_(NULL),
root_window_(NULL),
shelf_(NULL),
system_tray_height_(0) {
}
WorkAreaObserver::~WorkAreaObserver() {
StopObserving();
}
void WorkAreaObserver::SetSystemTrayHeight(int height) {
system_tray_height_ = height;
// If the shelf is shown during auto-hide state, the distance from the edge
// should be reduced by the height of shelf's shown height.
if (shelf_ && shelf_->visibility_state() == SHELF_AUTO_HIDE &&
shelf_->auto_hide_state() == SHELF_AUTO_HIDE_SHOWN) {
system_tray_height_ -= kShelfSize - ShelfLayoutManager::kAutoHideSize;
}
if (system_tray_height_ > 0)
system_tray_height_ += message_center::kMarginBetweenItems;
if (!shelf_)
return;
OnAutoHideStateChanged(shelf_->auto_hide_state());
}
void WorkAreaObserver::StartObserving(
message_center::MessagePopupCollection* collection,
aura::Window* root_window) {
DCHECK(collection);
collection_ = collection;
root_window_ = root_window;
UpdateShelf();
Shell::GetInstance()->AddShellObserver(this);
if (system_tray_height_ > 0)
OnAutoHideStateChanged(shelf_->auto_hide_state());
}
void WorkAreaObserver::StopObserving() {
Shell::GetInstance()->RemoveShellObserver(this);
if (shelf_)
shelf_->RemoveObserver(this);
collection_ = NULL;
shelf_ = NULL;
}
void WorkAreaObserver::OnDisplayWorkAreaInsetsChanged() {
UpdateShelf();
collection_->OnDisplayMetricsChanged(
Shell::GetScreen()->GetDisplayNearestWindow(
shelf_->shelf_widget()->GetNativeView()),
gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
}
void WorkAreaObserver::OnAutoHideStateChanged(ShelfAutoHideState new_state) {
gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
shelf_->shelf_widget()->GetNativeView());
gfx::Rect work_area = display.work_area();
int width = 0;
if ((shelf_->visibility_state() == SHELF_AUTO_HIDE) &&
new_state == SHELF_AUTO_HIDE_SHOWN) {
// Since the work_area is already reduced by kAutoHideSize, the inset width
// should be just the difference.
width = kShelfSize - ShelfLayoutManager::kAutoHideSize;
}
work_area.Inset(shelf_->SelectValueForShelfAlignment(
gfx::Insets(0, 0, width, 0),
gfx::Insets(0, width, 0, 0),
gfx::Insets(0, 0, 0, width),
gfx::Insets(width, 0, 0, 0)));
if (system_tray_height_ > 0) {
work_area.set_height(
std::max(0, work_area.height() - system_tray_height_));
if (shelf_->GetAlignment() == SHELF_ALIGNMENT_TOP)
work_area.set_y(work_area.y() + system_tray_height_);
}
collection_->SetDisplayInfo(work_area, display.bounds());
}
void WorkAreaObserver::UpdateShelf() {
if (shelf_)
return;
shelf_ = ShelfLayoutManager::ForShelf(root_window_);
if (shelf_)
shelf_->AddObserver(this);
}
// Class to initialize and manage the WebNotificationBubble and
// TrayBubbleWrapper instances for a bubble.
class WebNotificationBubbleWrapper {
......@@ -308,25 +181,25 @@ WebNotificationTray::WebNotificationTray(StatusAreaWidget* status_area_widget)
message_center_tray_.reset(new message_center::MessageCenterTray(
this,
message_center::MessageCenter::Get()));
popup_alignment_delegate_.reset(new AshPopupAlignmentDelegate());
popup_collection_.reset(new message_center::MessagePopupCollection(
ash::Shell::GetContainer(
status_area_widget->GetNativeView()->GetRootWindow(),
kShellWindowId_StatusContainer),
message_center(),
message_center_tray_.get(),
true));
work_area_observer_.reset(new WorkAreaObserver());
work_area_observer_->StartObserving(
popup_collection_.get(),
status_area_widget->GetNativeView()->GetRootWindow());
popup_alignment_delegate_.get()));
const gfx::Display& display = Shell::GetScreen()->GetDisplayNearestWindow(
status_area_widget->GetNativeView());
popup_alignment_delegate_->StartObserving(Shell::GetScreen(), display);
OnMessageCenterTrayChanged();
}
WebNotificationTray::~WebNotificationTray() {
// Release any child views that might have back pointers before ~View().
message_center_bubble_.reset();
popup_alignment_delegate_.reset();
popup_collection_.reset();
work_area_observer_.reset();
}
// Public methods.
......@@ -398,7 +271,7 @@ void WebNotificationTray::HideMessageCenter() {
}
void WebNotificationTray::SetSystemTrayHeight(int height) {
work_area_observer_->SetSystemTrayHeight(height);
popup_alignment_delegate_->SetSystemTrayHeight(height);
}
bool WebNotificationTray::ShowPopups() {
......
......@@ -41,7 +41,7 @@ namespace ash {
class StatusAreaWidget;
class WebNotificationBubbleWrapper;
class WebNotificationButton;
class WorkAreaObserver;
class AshPopupAlignmentDelegate;
class ASH_EXPORT WebNotificationTray
: public TrayBackgroundView,
......@@ -175,8 +175,7 @@ class ASH_EXPORT WebNotificationTray
// flickers of the shelf from hidden to shown. See: crbug.com/181213
bool should_block_shelf_auto_hide_;
// Observes the work area for |popup_collection_| and notifies to it.
scoped_ptr<WorkAreaObserver> work_area_observer_;
scoped_ptr<AshPopupAlignmentDelegate> popup_alignment_delegate_;
DISALLOW_COPY_AND_ASSIGN(WebNotificationTray);
};
......
......@@ -14,6 +14,7 @@
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_item.h"
#include "ash/system/web_notification/ash_popup_alignment_delegate.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/status_area_widget_test_helper.h"
#include "ash/test/test_system_tray_delegate.h"
......@@ -134,12 +135,12 @@ class WebNotificationTrayTest : public test::AshTestBase {
return GetTray()->GetWidget();
}
gfx::Rect GetPopupWorkArea() {
return GetPopupWorkAreaForTray(GetTray());
int GetPopupWorkAreaBottom() {
return GetPopupWorkAreaBottomForTray(GetTray());
}
gfx::Rect GetPopupWorkAreaForTray(WebNotificationTray* tray) {
return tray->popup_collection_->work_area_;
int GetPopupWorkAreaBottomForTray(WebNotificationTray* tray) {
return tray->popup_alignment_delegate_->GetWorkAreaBottom();
}
bool IsPopupVisible() {
......@@ -319,44 +320,40 @@ TEST_F(WebNotificationTrayTest, MAYBE_PopupAndSystemTray) {
AddNotification("test_id");
EXPECT_TRUE(GetTray()->IsPopupVisible());
gfx::Rect work_area = GetPopupWorkArea();
int bottom = GetPopupWorkAreaBottom();
// System tray is created, the popup's work area should be narrowed but still
// visible.
GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
EXPECT_TRUE(GetTray()->IsPopupVisible());
gfx::Rect work_area_with_tray = GetPopupWorkArea();
EXPECT_GT(work_area.size().GetArea(), work_area_with_tray.size().GetArea());
int bottom_with_tray = GetPopupWorkAreaBottom();
EXPECT_GT(bottom, bottom_with_tray);
// System tray notification is also created, the popup's work area is narrowed
// even more, but still visible.
GetSystemTray()->ShowNotificationView(test_item);
EXPECT_TRUE(GetTray()->IsPopupVisible());
gfx::Rect work_area_with_tray_notification = GetPopupWorkArea();
EXPECT_GT(work_area.size().GetArea(),
work_area_with_tray_notification.size().GetArea());
EXPECT_GT(work_area_with_tray.size().GetArea(),
work_area_with_tray_notification.size().GetArea());
int bottom_with_tray_notification = GetPopupWorkAreaBottom();
EXPECT_GT(bottom, bottom_with_tray_notification);
EXPECT_GT(bottom_with_tray, bottom_with_tray_notification);
// Close system tray, only system tray notifications.
GetSystemTray()->ClickedOutsideBubble();
EXPECT_TRUE(GetTray()->IsPopupVisible());
gfx::Rect work_area_with_notification = GetPopupWorkArea();
EXPECT_GT(work_area.size().GetArea(),
work_area_with_notification.size().GetArea());
EXPECT_LT(work_area_with_tray_notification.size().GetArea(),
work_area_with_notification.size().GetArea());
int bottom_with_notification = GetPopupWorkAreaBottom();
EXPECT_GT(bottom, bottom_with_notification);
EXPECT_LT(bottom_with_tray_notification, bottom_with_notification);
// Close the system tray notifications.
GetSystemTray()->HideNotificationView(test_item);
EXPECT_TRUE(GetTray()->IsPopupVisible());
EXPECT_EQ(work_area.ToString(), GetPopupWorkArea().ToString());
EXPECT_EQ(bottom, GetPopupWorkAreaBottom());
}
TEST_F(WebNotificationTrayTest, MAYBE_PopupAndAutoHideShelf) {
AddNotification("test_id");
EXPECT_TRUE(GetTray()->IsPopupVisible());
gfx::Rect work_area = GetPopupWorkArea();
int bottom = GetPopupWorkAreaBottom();
// Shelf's auto-hide state won't be HIDDEN unless window exists.
scoped_ptr<aura::Window> window(
......@@ -366,14 +363,14 @@ TEST_F(WebNotificationTrayTest, MAYBE_PopupAndAutoHideShelf) {
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
gfx::Rect work_area_auto_hidden = GetPopupWorkArea();
EXPECT_LT(work_area.size().GetArea(), work_area_auto_hidden.size().GetArea());
int bottom_auto_hidden = GetPopupWorkAreaBottom();
EXPECT_LT(bottom, bottom_auto_hidden);
// Close the window, which shows the shelf.
window.reset();
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
gfx::Rect work_area_auto_shown = GetPopupWorkArea();
EXPECT_EQ(work_area.ToString(), work_area_auto_shown.ToString());
int bottom_auto_shown = GetPopupWorkAreaBottom();
EXPECT_EQ(bottom, bottom_auto_shown);
// Create the system tray during auto-hide.
window.reset(CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4)));
......@@ -383,42 +380,38 @@ TEST_F(WebNotificationTrayTest, MAYBE_PopupAndAutoHideShelf) {
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
EXPECT_TRUE(GetTray()->IsPopupVisible());
gfx::Rect work_area_with_tray = GetPopupWorkArea();
EXPECT_GT(work_area_auto_shown.size().GetArea(),
work_area_with_tray.size().GetArea());
int bottom_with_tray = GetPopupWorkAreaBottom();
EXPECT_GT(bottom_auto_shown, bottom_with_tray);
// Create tray notification.
GetSystemTray()->ShowNotificationView(test_item);
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
gfx::Rect work_area_with_tray_notification = GetPopupWorkArea();
EXPECT_GT(work_area_with_tray.size().GetArea(),
work_area_with_tray_notification.size().GetArea());
int bottom_with_tray_notification = GetPopupWorkAreaBottom();
EXPECT_GT(bottom_with_tray, bottom_with_tray_notification);
// Close the system tray.
GetSystemTray()->ClickedOutsideBubble();
shelf->UpdateAutoHideState();
RunAllPendingInMessageLoop();
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
gfx::Rect work_area_hidden_with_tray_notification = GetPopupWorkArea();
EXPECT_LT(work_area_with_tray_notification.size().GetArea(),
work_area_hidden_with_tray_notification.size().GetArea());
EXPECT_GT(work_area_auto_hidden.size().GetArea(),
work_area_hidden_with_tray_notification.size().GetArea());
int bottom_hidden_with_tray_notification = GetPopupWorkAreaBottom();
EXPECT_LT(bottom_with_tray_notification,
bottom_hidden_with_tray_notification);
EXPECT_GT(bottom_auto_hidden, bottom_hidden_with_tray_notification);
// Close the window again, which shows the shelf.
window.reset();
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
gfx::Rect work_area_shown_with_tray_notification = GetPopupWorkArea();
EXPECT_GT(work_area_hidden_with_tray_notification.size().GetArea(),
work_area_shown_with_tray_notification.size().GetArea());
EXPECT_GT(work_area_auto_shown.size().GetArea(),
work_area_shown_with_tray_notification.size().GetArea());
int bottom_shown_with_tray_notification = GetPopupWorkAreaBottom();
EXPECT_GT(bottom_hidden_with_tray_notification,
bottom_shown_with_tray_notification);
EXPECT_GT(bottom_auto_shown, bottom_shown_with_tray_notification);
}
TEST_F(WebNotificationTrayTest, MAYBE_PopupAndFullscreen) {
AddNotification("test_id");
EXPECT_TRUE(IsPopupVisible());
gfx::Rect work_area = GetPopupWorkArea();
int bottom = GetPopupWorkAreaBottom();
// Checks the work area for normal auto-hidden state.
scoped_ptr<aura::Window> window(
......@@ -427,7 +420,7 @@ TEST_F(WebNotificationTrayTest, MAYBE_PopupAndFullscreen) {
Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
gfx::Rect work_area_auto_hidden = GetPopupWorkArea();
int bottom_auto_hidden = GetPopupWorkAreaBottom();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
// Put |window| into fullscreen without forcing the shelf to hide. Currently,
......@@ -440,9 +433,8 @@ TEST_F(WebNotificationTrayTest, MAYBE_PopupAndFullscreen) {
// The work area for auto-hidden status of fullscreen is a bit larger
// since it doesn't even have the 3-pixel width.
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
gfx::Rect work_area_fullscreen_hidden = GetPopupWorkArea();
EXPECT_EQ(work_area_auto_hidden.ToString(),
work_area_fullscreen_hidden.ToString());
int bottom_fullscreen_hidden = GetPopupWorkAreaBottom();
EXPECT_EQ(bottom_auto_hidden, bottom_fullscreen_hidden);
// Move the mouse cursor at the bottom, which shows the shelf.
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
......@@ -452,27 +444,27 @@ TEST_F(WebNotificationTrayTest, MAYBE_PopupAndFullscreen) {
generator.MoveMouseTo(bottom_right);
shelf->UpdateAutoHideStateNow();
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
EXPECT_EQ(work_area.ToString(), GetPopupWorkArea().ToString());
EXPECT_EQ(bottom, GetPopupWorkAreaBottom());
generator.MoveMouseTo(work_area.CenterPoint());
generator.MoveMouseTo(
Shell::GetScreen()->GetPrimaryDisplay().bounds().CenterPoint());
shelf->UpdateAutoHideStateNow();
EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
EXPECT_EQ(work_area_auto_hidden.ToString(), GetPopupWorkArea().ToString());
EXPECT_EQ(bottom_auto_hidden, GetPopupWorkAreaBottom());
}
TEST_F(WebNotificationTrayTest, MAYBE_PopupAndSystemTrayMultiDisplay) {
UpdateDisplay("800x600,600x400");
AddNotification("test_id");
gfx::Rect work_area = GetPopupWorkArea();
gfx::Rect work_area_second = GetPopupWorkAreaForTray(GetSecondaryTray());
int bottom = GetPopupWorkAreaBottom();
int bottom_second = GetPopupWorkAreaBottomForTray(GetSecondaryTray());
// System tray is created on the primary display. The popups in the secondary
// tray aren't affected.
GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
EXPECT_GT(work_area.size().GetArea(), GetPopupWorkArea().size().GetArea());
EXPECT_EQ(work_area_second.ToString(),
GetPopupWorkAreaForTray(GetSecondaryTray()).ToString());
EXPECT_GT(bottom, GetPopupWorkAreaBottom());
EXPECT_EQ(bottom_second, GetPopupWorkAreaBottomForTray(GetSecondaryTray()));
}
} // namespace ash
......@@ -27,6 +27,7 @@
#include "ui/gfx/size.h"
#include "ui/message_center/message_center_tray.h"
#include "ui/message_center/message_center_tray_delegate.h"
#include "ui/message_center/views/desktop_popup_alignment_delegate.h"
#include "ui/message_center/views/message_popup_collection.h"
#include "ui/views/widget/widget.h"
......@@ -135,8 +136,10 @@ WebNotificationTray::WebNotificationTray(PrefService* local_state)
message_center_tray_.reset(
new MessageCenterTray(this, g_browser_process->message_center()));
last_quiet_mode_state_ = message_center()->IsQuietMode();
alignment_delegate_.reset(new message_center::DesktopPopupAlignmentDelegate);
popup_collection_.reset(new message_center::MessagePopupCollection(
NULL, message_center(), message_center_tray_.get(), false));
NULL, message_center(), message_center_tray_.get(),
alignment_delegate_.get()));
#if defined(OS_WIN)
// |local_state| can be NULL in tests.
......@@ -164,6 +167,7 @@ message_center::MessageCenter* WebNotificationTray::message_center() {
}
bool WebNotificationTray::ShowPopups() {
alignment_delegate_->StartObserving(gfx::Screen::GetNativeScreen());
popup_collection_->DoUpdateIfPossible();
return true;
}
......
......@@ -38,6 +38,7 @@ namespace message_center {
struct PositionInfo;
class DesktopPopupAlignmentDelegate;
class MessageCenterWidgetDelegate;
// A MessageCenterTrayDelegate implementation that exposes the MessageCenterTray
......@@ -113,6 +114,7 @@ class WebNotificationTray : public message_center::MessageCenterTrayDelegate,
MessageCenterWidgetDelegate* message_center_delegate_;
scoped_ptr<message_center::MessagePopupCollection> popup_collection_;
scoped_ptr<message_center::DesktopPopupAlignmentDelegate> alignment_delegate_;
StatusIcon* status_icon_;
StatusIconMenuModel* status_icon_menu_;
......
......@@ -72,6 +72,8 @@
'views/bounded_label.cc',
'views/bounded_label.h',
'views/constants.h',
'views/desktop_popup_alignment_delegate.cc',
'views/desktop_popup_alignment_delegate.h',
'views/message_bubble_base.cc',
'views/message_bubble_base.h',
'views/message_center_controller.h',
......@@ -95,6 +97,8 @@
'views/notification_view.h',
'views/padded_button.cc',
'views/padded_button.h',
'views/popup_alignment_delegate.cc',
'views/popup_alignment_delegate.h',
'views/proportional_image_view.cc',
'views/proportional_image_view.h',
'views/toast_contents_view.cc',
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/message_center/views/desktop_popup_alignment_delegate.h"
#include "ui/gfx/display.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/screen.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/message_popup_collection.h"
namespace message_center {
DesktopPopupAlignmentDelegate::DesktopPopupAlignmentDelegate()
: alignment_(POPUP_ALIGNMENT_BOTTOM | POPUP_ALIGNMENT_RIGHT),
display_id_(gfx::Display::kInvalidDisplayID),
screen_(NULL) {
}
DesktopPopupAlignmentDelegate::~DesktopPopupAlignmentDelegate() {
if (screen_)
screen_->RemoveObserver(this);
}
void DesktopPopupAlignmentDelegate::StartObserving(gfx::Screen* screen) {
if (screen_ || !screen)
return;
screen_ = screen;
screen_->AddObserver(this);
gfx::Display display = screen_->GetPrimaryDisplay();
display_id_ = display.id();
RecomputeAlignment(display);
}
int DesktopPopupAlignmentDelegate::GetToastOriginX(
const gfx::Rect& toast_bounds) const {
if (IsFromLeft())
return work_area_.x() + kMarginBetweenItems;
return work_area_.right() - kMarginBetweenItems - toast_bounds.width();
}
int DesktopPopupAlignmentDelegate::GetBaseLine() const {
return IsTopDown()
? work_area_.y() + kMarginBetweenItems
: work_area_.bottom() - kMarginBetweenItems;
}
int DesktopPopupAlignmentDelegate::GetWorkAreaBottom() const {
return work_area_.bottom();
}
bool DesktopPopupAlignmentDelegate::IsTopDown() const {
return (alignment_ & POPUP_ALIGNMENT_TOP) != 0;
}
bool DesktopPopupAlignmentDelegate::IsFromLeft() const {
return (alignment_ & POPUP_ALIGNMENT_LEFT) != 0;
}
void DesktopPopupAlignmentDelegate::RecomputeAlignment(
const gfx::Display& display) {
if (work_area_ == display.work_area())
return;
work_area_ = display.work_area();
// If the taskbar is at the top, render notifications top down. Some platforms
// like Gnome can have taskbars at top and bottom. In this case it's more
// likely that the systray is on the top one.
alignment_ = work_area_.y() > display.bounds().y() ? POPUP_ALIGNMENT_TOP
: POPUP_ALIGNMENT_BOTTOM;
// If the taskbar is on the left show the notifications on the left. Otherwise
// show it on right since it's very likely that the systray is on the right if
// the taskbar is on the top or bottom.
// Since on some platforms like Ubuntu Unity there's also a launcher along
// with a taskbar (panel), we need to check that there is really nothing at
// the top before concluding that the taskbar is at the left.
alignment_ |= (work_area_.x() > display.bounds().x() &&
work_area_.y() == display.bounds().y())
? POPUP_ALIGNMENT_LEFT
: POPUP_ALIGNMENT_RIGHT;
}
void DesktopPopupAlignmentDelegate::OnDisplayAdded(
const gfx::Display& new_display) {
}
void DesktopPopupAlignmentDelegate::OnDisplayRemoved(
const gfx::Display& old_display) {
}
void DesktopPopupAlignmentDelegate::OnDisplayMetricsChanged(
const gfx::Display& display,
uint32_t metrics) {
if (display.id() == display_id_) {
RecomputeAlignment(display);
DoUpdateIfPossible();
}
}
} // namespace message_center
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_MESSAGE_CENTER_VIEWS_DESKTOP_POPUP_ALIGNMENT_DELEGATE_H_
#define UI_MESSAGE_CENTER_VIEWS_DESKTOP_POPUP_ALIGNMENT_DELEGATE_H_
#include "base/macros.h"
#include "ui/gfx/display_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/message_center/views/popup_alignment_delegate.h"
namespace gfx {
class Screen;
}
namespace message_center {
namespace test {
class MessagePopupCollectionTest;
}
// The PopupAlignmentDelegate for non-ash Windows/Linux desktop.
class MESSAGE_CENTER_EXPORT DesktopPopupAlignmentDelegate
: public PopupAlignmentDelegate,
public gfx::DisplayObserver {
public:
DesktopPopupAlignmentDelegate();
virtual ~DesktopPopupAlignmentDelegate();
void StartObserving(gfx::Screen* screen);
// Overridden from PopupAlignmentDelegate:
virtual int GetToastOriginX(const gfx::Rect& toast_bounds) const OVERRIDE;
virtual int GetBaseLine() const OVERRIDE;
virtual int GetWorkAreaBottom() const OVERRIDE;
virtual bool IsTopDown() const OVERRIDE;
virtual bool IsFromLeft() const OVERRIDE;
virtual void RecomputeAlignment(const gfx::Display& display) OVERRIDE;
private:
friend class test::MessagePopupCollectionTest;
enum PopupAlignment {
POPUP_ALIGNMENT_TOP = 1 << 0,
POPUP_ALIGNMENT_LEFT = 1 << 1,
POPUP_ALIGNMENT_BOTTOM = 1 << 2,
POPUP_ALIGNMENT_RIGHT = 1 << 3,
};
// Overridden from gfx::DisplayObserver:
virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
virtual void OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t metrics) OVERRIDE;
int32_t alignment_;
int64_t display_id_;
gfx::Screen* screen_;
gfx::Rect work_area_;
DISALLOW_COPY_AND_ASSIGN(DesktopPopupAlignmentDelegate);
};
} // namespace message_center
#endif // UI_MESSAGE_CENTER_VIEWS_DESKTOP_POPUP_ALIGNMENT_DELEGATE_H_
......@@ -12,8 +12,6 @@
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/gfx/display.h"
#include "ui/gfx/display_observer.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/rect.h"
#include "ui/message_center/message_center_export.h"
......@@ -30,12 +28,8 @@ namespace views {
class Widget;
}
namespace ash {
class WebNotificationTrayTest;
FORWARD_DECLARE_TEST(WebNotificationTrayTest, ManyPopupNotifications);
}
namespace gfx {
class Display;
class Screen;
}
......@@ -47,13 +41,7 @@ class MessagePopupCollectionTest;
class MessageCenter;
class MessageCenterTray;
class MessageViewContextMenuController;
enum PopupAlignment {
POPUP_ALIGNMENT_TOP = 1 << 0,
POPUP_ALIGNMENT_LEFT = 1 << 1,
POPUP_ALIGNMENT_BOTTOM = 1 << 2,
POPUP_ALIGNMENT_RIGHT = 1 << 3,
};
class PopupAlignmentDelegate;
// Container for popup toasts. Because each toast is a frameless window rather
// than a view in a bubble, now the container just manages all of those toasts.
......@@ -62,17 +50,15 @@ enum PopupAlignment {
// be slightly different.
class MESSAGE_CENTER_EXPORT MessagePopupCollection
: public MessageCenterController,
public MessageCenterObserver,
public gfx::DisplayObserver {
public MessageCenterObserver {
public:
// |parent| specifies the parent widget of the toast windows. The default
// parent will be used for NULL. Usually each icon is spacing against its
// predecessor. If |first_item_has_no_margin| is set however the first item
// does not space against the tray.
// predecessor.
MessagePopupCollection(gfx::NativeView parent,
MessageCenter* message_center,
MessageCenterTray* tray,
bool first_item_has_no_margin);
PopupAlignmentDelegate* alignment_delegate);
virtual ~MessagePopupCollection();
// Overridden from MessageCenterController:
......@@ -109,30 +95,14 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection
// toast is irrevocably closed (such as within RemoveToast).
void ForgetToast(ToastContentsView* toast);
// Updates |work_area_| and re-calculates the alignment of notification toasts
// rearranging them if necessary.
// This is separated from methods from OnDisplayMetricsChanged(), since
// sometimes the display info has to be specified directly. One example is
// shelf's auto-hide change. When the shelf in ChromeOS is temporarily shown
// from auto hide status, it doesn't change the display's work area but the
// actual work area for toasts should be resized.
void SetDisplayInfo(const gfx::Rect& work_area,
const gfx::Rect& screen_bounds);
// Overridden from gfx::DislayObserver:
virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
virtual void OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t metrics) OVERRIDE;
// Called when the display bounds has been changed. Used in Windows only.
void OnDisplayMetricsChanged(const gfx::Display& display);
// Used by ToastContentsView to locate itself.
gfx::NativeView parent() const { return parent_; }
private:
FRIEND_TEST_ALL_PREFIXES(ash::WebNotificationTrayTest,
ManyPopupNotifications);
friend class test::MessagePopupCollectionTest;
friend class ash::WebNotificationTrayTest;
typedef std::list<ToastContentsView*> Toasts;
// Iterates toasts and starts closing them.
......@@ -141,9 +111,6 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection
// Called by ToastContentsView when its window is closed.
void RemoveToast(ToastContentsView* toast, bool mark_as_shown);
// Returns the x-origin for the given toast bounds in the current work area.
int GetToastOriginX(const gfx::Rect& toast_bounds) const;
// Creates new widgets for new toast notifications, and updates |toasts_| and
// |widgets_| correctly.
void UpdateWidgets();
......@@ -156,8 +123,6 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection
// See crbug.com/224089
void RepositionWidgetsWithTarget();
void ComputePopupAlignment(gfx::Rect work_area, gfx::Rect screen_bounds);
// The base line is an (imaginary) line that would touch the bottom of the
// next created notification if bottom-aligned or its top if top-aligned.
int GetBaseLine(ToastContentsView* last_toast) const;
......@@ -187,13 +152,8 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection
MessageCenter* message_center_;
MessageCenterTray* tray_;
Toasts toasts_;
gfx::Rect work_area_;
int64 display_id_;
gfx::Screen* screen_;
// Specifies which corner of the screen popups should show up. This should
// ideally be the same corner the notification area (systray) is at.
PopupAlignment alignment_;
PopupAlignmentDelegate* alignment_delegate_;
int defer_counter_;
......@@ -215,9 +175,6 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection
// Weak, only exists temporarily in tests.
scoped_ptr<base::RunLoop> run_loop_for_test_;
// True if the first item should not have spacing against the tray.
bool first_item_has_no_margin_;
scoped_ptr<MessageViewContextMenuController> context_menu_controller_;
// Gives out weak pointers to toast contents views which have an unrelated
......
......@@ -15,6 +15,7 @@
#include "ui/gfx/display.h"
#include "ui/gfx/rect.h"
#include "ui/message_center/fake_message_center.h"
#include "ui/message_center/views/desktop_popup_alignment_delegate.h"
#include "ui/message_center/views/toast_contents_view.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"
......@@ -29,15 +30,14 @@ class MessagePopupCollectionTest : public views::ViewsTestBase {
views::ViewsTestBase::SetUp();
MessageCenter::Initialize();
MessageCenter::Get()->DisableTimersForTest();
alignment_delegate_.reset(new DesktopPopupAlignmentDelegate);
collection_.reset(new MessagePopupCollection(
GetContext(), MessageCenter::Get(), NULL, false));
GetContext(), MessageCenter::Get(), NULL, alignment_delegate_.get()));
// This size fits test machines resolution and also can keep a few toasts
// w/o ill effects of hitting the screen overflow. This allows us to assume
// and verify normal layout of the toast stack.
collection_->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),
gfx::Rect(0, 0, 600, 400)); // Simulate a
// taskbar at the
// bottom.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // taskbar at the bottom.
gfx::Rect(0, 0, 600, 400));
id_ = 0;
PrepareForWait();
}
......@@ -68,8 +68,17 @@ class MessagePopupCollectionTest : public views::ViewsTestBase {
return collection_->GetWidgetForTest(id);
}
void SetDisplayInfo(const gfx::Rect& work_area,
const gfx::Rect& display_bounds) {
gfx::Display dummy_display;
dummy_display.set_bounds(display_bounds);
dummy_display.set_work_area(work_area);
alignment_delegate_->RecomputeAlignment(dummy_display);
PrepareForWait();
}
gfx::Rect GetWorkArea() {
return collection_->work_area_;
return alignment_delegate_->work_area_;
}
ToastContentsView* GetToast(const std::string& id) {
......@@ -118,6 +127,7 @@ class MessagePopupCollectionTest : public views::ViewsTestBase {
private:
scoped_ptr<MessagePopupCollection> collection_;
scoped_ptr<DesktopPopupAlignmentDelegate> alignment_delegate_;
int id_;
};
......@@ -201,8 +211,8 @@ TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
// If taskbar is on the right we show the toasts bottom to top as usual.
// Simulate a taskbar at the right.
collection()->SetDisplayInfo(gfx::Rect(0, 0, 590, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 0, 590, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
......@@ -223,14 +233,14 @@ TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
EXPECT_EQ(0u, GetToastCounts());
// Restore simulated taskbar position to bottom.
collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
}
TEST_F(MessagePopupCollectionTest, TopDownPositioningWithTopTaskbar) {
// Simulate a taskbar at the top.
collection()->SetDisplayInfo(gfx::Rect(0, 10, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 10, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
......@@ -251,8 +261,8 @@ TEST_F(MessagePopupCollectionTest, TopDownPositioningWithTopTaskbar) {
EXPECT_EQ(0u, GetToastCounts());
// Restore simulated taskbar position to bottom.
collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
}
TEST_F(MessagePopupCollectionTest, TopDownPositioningWithLeftAndTopTaskbar) {
......@@ -260,8 +270,8 @@ TEST_F(MessagePopupCollectionTest, TopDownPositioningWithLeftAndTopTaskbar) {
// assumed that the actual taskbar is the top one.
// Simulate a taskbar at the top and left.
collection()->SetDisplayInfo(gfx::Rect(10, 10, 590, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(10, 10, 590, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
......@@ -282,8 +292,8 @@ TEST_F(MessagePopupCollectionTest, TopDownPositioningWithLeftAndTopTaskbar) {
EXPECT_EQ(0u, GetToastCounts());
// Restore simulated taskbar position to bottom.
collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
}
TEST_F(MessagePopupCollectionTest, TopDownPositioningWithBottomAndTopTaskbar) {
......@@ -291,8 +301,8 @@ TEST_F(MessagePopupCollectionTest, TopDownPositioningWithBottomAndTopTaskbar) {
// assumed that the actual taskbar is the top one.
// Simulate a taskbar at the top and bottom.
collection()->SetDisplayInfo(gfx::Rect(0, 10, 580, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 10, 580, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
......@@ -313,14 +323,14 @@ TEST_F(MessagePopupCollectionTest, TopDownPositioningWithBottomAndTopTaskbar) {
EXPECT_EQ(0u, GetToastCounts());
// Restore simulated taskbar position to bottom.
collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
}
TEST_F(MessagePopupCollectionTest, LeftPositioningWithLeftTaskbar) {
// Simulate a taskbar at the left.
collection()->SetDisplayInfo(gfx::Rect(10, 0, 590, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(10, 0, 590, 400), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
std::string id0 = AddNotification();
std::string id1 = AddNotification();
WaitForTransitionsDone();
......@@ -344,8 +354,8 @@ TEST_F(MessagePopupCollectionTest, LeftPositioningWithLeftTaskbar) {
EXPECT_EQ(0u, GetToastCounts());
// Restore simulated taskbar position to bottom.
collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
gfx::Rect(0, 0, 600, 400)); // Display-bounds.
}
TEST_F(MessagePopupCollectionTest, DetectMouseHover) {
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/message_center/views/popup_alignment_delegate.h"
#include "ui/message_center/views/message_popup_collection.h"
namespace message_center {
PopupAlignmentDelegate::PopupAlignmentDelegate() : collection_(NULL) {}
PopupAlignmentDelegate::~PopupAlignmentDelegate() {}
void PopupAlignmentDelegate::DoUpdateIfPossible() {
if (collection_)
collection_->DoUpdateIfPossible();
}
} // namespace message_center
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_MESSAGE_CENTER_VIEWS_POPUP_ALIGNMENT_DELEGATE_H_
#define UI_MESSAGE_CENTER_VIEWS_POPUP_ALIGNMENT_DELEGATE_H_
#include "ui/message_center/message_center_export.h"
namespace gfx {
class Display;
class Point;
class Rect;
}
namespace message_center {
class MessagePopupCollection;
class MESSAGE_CENTER_EXPORT PopupAlignmentDelegate {
public:
PopupAlignmentDelegate();
void set_collection(MessagePopupCollection* collection) {
collection_ = collection;
}
// Returns the x-origin for the given toast bounds in the current work area.
virtual int GetToastOriginX(const gfx::Rect& toast_bounds) const = 0;
// Returns the baseline height of the current work area. That is the starting
// point if there are no other toasts.
virtual int GetBaseLine() const = 0;
// Returns the height of the bottom of the current work area.
virtual int GetWorkAreaBottom() const = 0;
// Returns true if the toast should be aligned top down.
virtual bool IsTopDown() const = 0;
// Returns true if the toasts are positioned at the left side of the desktop
// so that their reveal animation should happen from left side.
virtual bool IsFromLeft() const = 0;
// Called when a new toast appears or toasts are rearranged in the |display|.
// The subclass may override this method to check the current desktop status
// so that the toasts are arranged at the correct place.
virtual void RecomputeAlignment(const gfx::Display& display) = 0;
protected:
virtual ~PopupAlignmentDelegate();
void DoUpdateIfPossible();
private:
MessagePopupCollection* collection_;
};
} // namespace message_center
#endif // UI_MESSAGE_CENTER_VIEWS_POPUP_ALIGNMENT_DELEGATE_H_
......@@ -246,9 +246,7 @@ void ToastContentsView::OnDisplayChanged() {
return;
collection_->OnDisplayMetricsChanged(
Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view),
gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS |
gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view));
}
void ToastContentsView::OnWorkAreaChanged() {
......@@ -261,8 +259,7 @@ void ToastContentsView::OnWorkAreaChanged() {
return;
collection_->OnDisplayMetricsChanged(
Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view),
gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
Screen::GetScreenFor(native_view)->GetDisplayNearestWindow(native_view));
}
// views::View
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment