summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos
diff options
context:
space:
mode:
authoroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-16 23:59:55 +0000
committeroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-16 23:59:55 +0000
commitb253e636b63e9383271fdeb2858f416d59922b57 (patch)
treebdb8de9fcf6ffa25e2b4070e2e2d065be3cf4af4 /chrome/browser/chromeos
parent45a613edf3b4cd4566cf631424217b59c75c146d (diff)
downloadchromium_src-b253e636b63e9383271fdeb2858f416d59922b57.zip
chromium_src-b253e636b63e9383271fdeb2858f416d59922b57.tar.gz
chromium_src-b253e636b63e9383271fdeb2858f416d59922b57.tar.bz2
Hover buttons for notification.
* Chagned WidgetGtk so that any GtkFixed can be parent of WidgetGtk. * Close button and Options menu is moved to separate TYPE_CHILD WidgetGtk which is shown/hidden as mouse moves. TODO: host Widget is not transparent right now. I'll address this in separate CL. BUG=41011 TEST=none Review URL: http://codereview.chromium.org/1654006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44861 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r--chrome/browser/chromeos/notifications/balloon_collection_impl.h7
-rw-r--r--chrome/browser/chromeos/notifications/balloon_view.cc309
-rw-r--r--chrome/browser/chromeos/notifications/balloon_view.h58
-rw-r--r--chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc1
-rw-r--r--chrome/browser/chromeos/notifications/notification_browsertest.cc2
-rw-r--r--chrome/browser/chromeos/notifications/notification_panel.cc73
-rw-r--r--chrome/browser/chromeos/notifications/notification_panel.h32
7 files changed, 326 insertions, 156 deletions
diff --git a/chrome/browser/chromeos/notifications/balloon_collection_impl.h b/chrome/browser/chromeos/notifications/balloon_collection_impl.h
index a7c2051..013cfcd 100644
--- a/chrome/browser/chromeos/notifications/balloon_collection_impl.h
+++ b/chrome/browser/chromeos/notifications/balloon_collection_impl.h
@@ -20,6 +20,8 @@ class Size;
namespace chromeos {
+class BalloonViewImpl;
+
// A balloon collection represents a set of notification balloons being
// shown in the chromeos notification panel. Unlike other platforms,
// chromeos shows the all notifications in the notification panel, and
@@ -38,8 +40,13 @@ class BalloonCollectionImpl : public BalloonCollection,
virtual void Add(Balloon* balloon) = 0;
virtual bool Update(Balloon* balloon) = 0;
virtual void Remove(Balloon* balloon) = 0;
+
+ // Resize notification from webkit.
virtual void ResizeNotification(Balloon* balloon,
const gfx::Size& size) = 0;
+
+ // Sets the active view.
+ virtual void SetActiveView(BalloonViewImpl* view) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(NotificationUI);
};
diff --git a/chrome/browser/chromeos/notifications/balloon_view.cc b/chrome/browser/chromeos/notifications/balloon_view.cc
index af03ad3..a59219f 100644
--- a/chrome/browser/chromeos/notifications/balloon_view.cc
+++ b/chrome/browser/chromeos/notifications/balloon_view.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "app/l10n_util.h"
+#include "app/menus/simple_menu_model.h"
#include "app/resource_bundle.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
@@ -22,36 +23,171 @@
#include "chrome/common/notification_type.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
+#include "views/background.h"
+#include "views/controls/button/button.h"
+#include "views/controls/button/image_button.h"
#include "views/controls/button/menu_button.h"
-#include "views/controls/button/text_button.h"
+#include "views/controls/label.h"
#include "views/controls/menu/menu_2.h"
+#include "views/controls/menu/view_menu_delegate.h"
#include "views/widget/root_view.h"
+#include "views/widget/widget_gtk.h"
namespace {
// Menu commands
const int kRevokePermissionCommand = 0;
+// Vertical margin between close button and menu button.
+const int kControlButtonsMargin = 4;
} // namespace
namespace chromeos {
+// NotificationControlView has close and menu buttons and
+// overlays on top of renderer view.
+class NotificationControlView : public views::View,
+ public views::ViewMenuDelegate,
+ public menus::SimpleMenuModel::Delegate,
+ public views::ButtonListener {
+ public:
+ explicit NotificationControlView(BalloonViewImpl* view)
+ : balloon_view_(view),
+ close_button_(NULL),
+ options_menu_contents_(NULL),
+ options_menu_menu_(NULL),
+ options_menu_button_(NULL) {
+ // TODO(oshima): make background transparent.
+ set_background(views::Background::CreateSolidBackground(SK_ColorWHITE));
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ SkBitmap* close_button_n = rb.GetBitmapNamed(IDR_TAB_CLOSE);
+ SkBitmap* close_button_m = rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK);
+ SkBitmap* close_button_h = rb.GetBitmapNamed(IDR_TAB_CLOSE_H);
+ SkBitmap* close_button_p = rb.GetBitmapNamed(IDR_TAB_CLOSE_P);
+
+ close_button_ = new views::ImageButton(this);
+ close_button_->SetImage(views::CustomButton::BS_NORMAL, close_button_n);
+ close_button_->SetImage(views::CustomButton::BS_HOT, close_button_h);
+ close_button_->SetImage(views::CustomButton::BS_PUSHED, close_button_p);
+ close_button_->SetBackground(
+ SK_ColorBLACK, close_button_n, close_button_m);
+
+ AddChildView(close_button_);
+
+ options_menu_button_
+ = new views::MenuButton(NULL, std::wstring(), this, false);
+ options_menu_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
+ options_menu_button_->SetIcon(*rb.GetBitmapNamed(IDR_NOTIFICATION_MENU));
+ options_menu_button_->set_border(NULL);
+
+ options_menu_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT);
+ AddChildView(options_menu_button_);
+
+ // The control view will never be resized, so just layout once.
+ gfx::Size button_size = close_button_->GetPreferredSize();
+ close_button_->SetBounds(
+ 0, 0, button_size.width(), button_size.height());
+ gfx::Size options_size = options_menu_button_->GetPreferredSize();
+ options_menu_button_->SetBounds(
+ 0, button_size.height() + kControlButtonsMargin,
+ options_size.width(), options_size.height());
+
+ SizeToPreferredSize();
+ }
+
+ virtual gfx::Size GetPreferredSize() {
+ gfx::Rect total_bounds =
+ close_button_->bounds().Union(options_menu_button_->bounds());
+ return total_bounds.size();
+ }
+
+ // views::ViewMenuDelegate implements.
+ virtual void RunMenu(views::View* source, const gfx::Point& pt) {
+ CreateOptionsMenu();
+ options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
+ }
+
+ // views::ButtonListener implements.
+ virtual void ButtonPressed(views::Button* sender, const views::Event&) {
+ balloon_view_->Close(true);
+ }
+
+ // menus::SimpleMenuModel::Delegate impglements.
+ virtual bool IsCommandIdChecked(int /* command_id */) const {
+ // Nothing in the menu is checked.
+ return false;
+ }
+
+ virtual bool IsCommandIdEnabled(int /* command_id */) const {
+ // All the menu options are always enabled.
+ return true;
+ }
+
+ virtual bool GetAcceleratorForCommandId(
+ int /* command_id */, menus::Accelerator* /* accelerator */) {
+ // Currently no accelerators.
+ return false;
+ }
+
+ virtual void ExecuteCommand(int command_id) {
+ switch (command_id) {
+ case kRevokePermissionCommand:
+ balloon_view_->DenyPermission();
+ default:
+ NOTIMPLEMENTED();
+ }
+ }
+
+ private:
+ void CreateOptionsMenu() {
+ if (options_menu_contents_.get())
+ return;
+ const string16 source_label_text = WideToUTF16Hack(l10n_util::GetStringF(
+ IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
+ balloon_view_->balloon_->notification().display_source()));
+ const string16 label_text = WideToUTF16Hack(l10n_util::GetStringF(
+ IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE,
+ balloon_view_->balloon_->notification().display_source()));
+
+ options_menu_contents_.reset(new menus::SimpleMenuModel(this));
+ // TODO(oshima): Showing the source info in the menu for now.
+ // Figure out where to show the source info.
+ options_menu_contents_->AddItem(-1, source_label_text);
+ options_menu_contents_->AddItem(kRevokePermissionCommand, label_text);
+
+ options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get()));
+ }
+
+ BalloonViewImpl* balloon_view_;
+
+ views::ImageButton* close_button_;
+
+ // The options menu.
+ scoped_ptr<menus::SimpleMenuModel> options_menu_contents_;
+ scoped_ptr<views::Menu2> options_menu_menu_;
+ views::MenuButton* options_menu_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationControlView);
+};
+
BalloonViewImpl::BalloonViewImpl(bool sticky, bool controls)
: balloon_(NULL),
html_contents_(NULL),
method_factory_(this),
- close_button_(NULL),
- options_menu_contents_(NULL),
- options_menu_menu_(NULL),
- options_menu_button_(NULL),
stale_(false),
sticky_(sticky),
- controls_(controls) {
+ controls_(controls),
+ closed_(false) {
// This object is not to be deleted by the views hierarchy,
// as it is owned by the balloon.
set_parent_owned(false);
}
BalloonViewImpl::~BalloonViewImpl() {
+ if (control_view_host_.get()) {
+ control_view_host_->CloseNow();
+ }
if (html_contents_) {
html_contents_->Shutdown();
}
@@ -61,48 +197,10 @@ BalloonViewImpl::~BalloonViewImpl() {
// BallonViewImpl, BalloonView implementation.
void BalloonViewImpl::Show(Balloon* balloon) {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- const std::wstring source_label_text = l10n_util::GetStringF(
- IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
- balloon->notification().display_source());
- const std::wstring options_text =
- l10n_util::GetString(IDS_NOTIFICATION_OPTIONS_MENU_LABEL);
- const std::wstring dismiss_text =
- l10n_util::GetString(IDS_NOTIFICATION_BALLOON_DISMISS_LABEL);
balloon_ = balloon;
html_contents_ = new BalloonViewHost(balloon);
AddChildView(html_contents_->view());
- if (controls_) {
- close_button_ = new views::TextButton(this, dismiss_text);
- close_button_->SetIcon(*rb.GetBitmapNamed(IDR_BALLOON_CLOSE));
- close_button_->SetHoverIcon(*rb.GetBitmapNamed(IDR_BALLOON_CLOSE_HOVER));
- close_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
- close_button_->SetEnabledColor(SK_ColorWHITE);
- close_button_->SetHoverColor(SK_ColorDKGRAY);
- close_button_->set_alignment(views::TextButton::ALIGN_CENTER);
- close_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT);
- AddChildView(close_button_);
-
- options_menu_button_
- = new views::MenuButton(NULL, options_text, this, false);
- options_menu_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
- options_menu_button_->SetIcon(
- *rb.GetBitmapNamed(IDR_BALLOON_OPTIONS_ARROW));
- options_menu_button_->SetHoverIcon(
- *rb.GetBitmapNamed(IDR_BALLOON_OPTIONS_ARROW_HOVER));
- options_menu_button_->set_alignment(views::TextButton::ALIGN_CENTER);
- options_menu_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT);
- options_menu_button_->SetEnabledColor(SK_ColorWHITE);
- options_menu_button_->SetHoverColor(SK_ColorDKGRAY);
- AddChildView(options_menu_button_);
-
- source_label_ = new views::Label(source_label_text);
- source_label_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
- source_label_->SetColor(SK_ColorWHITE);
- source_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
- AddChildView(source_label_);
- }
notification_registrar_.Add(this,
NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon));
}
@@ -115,6 +213,7 @@ void BalloonViewImpl::Update() {
}
void BalloonViewImpl::Close(bool by_user) {
+ closed_ = true;
MessageLoop::current()->PostTask(
FROM_HERE,
method_factory_.NewRunnableMethod(
@@ -138,77 +237,30 @@ void BalloonViewImpl::RepositionToBalloon() {
// views::View interface overrides.
void BalloonViewImpl::Layout() {
- gfx::Size button_size;
- if (close_button_) {
- button_size = close_button_->GetPreferredSize();
- }
+ gfx::Size size = balloon_->content_size();
- SetBounds(x(), y(),
- balloon_->content_size().width(),
- balloon_->content_size().height() +
- button_size.height());
- int x = width() - button_size.width();
- int y = height() - button_size.height();
+ SetBounds(x(), y(), size.width(), size.height());
- html_contents_->view()->SetBounds(0, 0, width(), y);
+ html_contents_->view()->SetBounds(0, 0, size.width(), size.height());
if (html_contents_->render_view_host()) {
RenderWidgetHostView* view = html_contents_->render_view_host()->view();
if (view)
- view->SetSize(gfx::Size(width(), y));
+ view->SetSize(size);
}
- if (controls_) {
- close_button_->SetBounds(x, y, button_size.width(), button_size.height());
- x -= close_button_->GetPreferredSize().width();
- options_menu_button_->SetBounds(
- x, y, button_size.width(), button_size.height());
- source_label_->SetBounds(0, y, x, button_size.height());
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// views::ViewMenuDelegate implementation.
-
-void BalloonViewImpl::RunMenu(views::View* source, const gfx::Point& pt) {
- CreateOptionsMenu();
- options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// views::Button implementation.
-
-void BalloonViewImpl::ButtonPressed(views::Button* sender,
- const views::Event&) {
- Close(true);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// menus::SimpleMenuModel::Delegate implementation.
-
-bool BalloonViewImpl::IsCommandIdChecked(int /* command_id */) const {
- // Nothing in the menu is checked.
- return false;
-}
-
-bool BalloonViewImpl::IsCommandIdEnabled(int /* command_id */) const {
- // All the menu options are always enabled.
- return true;
-}
-
-bool BalloonViewImpl::GetAcceleratorForCommandId(
- int /* command_id */, menus::Accelerator* /* accelerator */) {
- // Currently no accelerators.
- return false;
}
-void BalloonViewImpl::ExecuteCommand(int command_id) {
- DesktopNotificationService* service =
- balloon_->profile()->GetDesktopNotificationService();
- switch (command_id) {
- case kRevokePermissionCommand:
- service->DenyPermission(balloon_->notification().origin_url());
- break;
- default:
- NOTIMPLEMENTED();
+void BalloonViewImpl::ViewHierarchyChanged(
+ bool is_add, View* parent, View* child) {
+ if (is_add && GetWidget() && !control_view_host_.get() && controls_) {
+ control_view_host_.reset(
+ new views::WidgetGtk(views::WidgetGtk::TYPE_CHILD));
+ control_view_host_->Init(GetParentNativeView(), gfx::Rect());
+ NotificationControlView* control = new NotificationControlView(this);
+ control_view_host_->set_delete_on_destroy(false);
+ control_view_host_->SetContentsView(control);
+ }
+ if (!is_add && GetWidget() && control_view_host_.get() && controls_) {
+ control_view_host_.release()->CloseNow();
}
}
@@ -231,30 +283,47 @@ void BalloonViewImpl::Observe(NotificationType type,
}
////////////////////////////////////////////////////////////////////////////////
-// BalloonViewImpl private.
-
-void BalloonViewImpl::CreateOptionsMenu() {
- if (options_menu_contents_.get())
- return;
+// BalloonViewImpl public.
- const string16 label_text = WideToUTF16Hack(l10n_util::GetStringF(
- IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE,
- this->balloon_->notification().display_source()));
+bool BalloonViewImpl::IsFor(const Notification& notification) const {
+ return balloon_->notification().IsSame(notification);
+}
- options_menu_contents_.reset(new menus::SimpleMenuModel(this));
- options_menu_contents_->AddItem(kRevokePermissionCommand, label_text);
+void BalloonViewImpl::Activated() {
+ DCHECK(control_view_host_.get());
+ // Get the size of Control View.
+ gfx::Size size =
+ control_view_host_->GetRootView()->GetChildViewAt(0)->GetPreferredSize();
+ control_view_host_->Show();
+ control_view_host_->SetBounds(
+ gfx::Rect(width() - size.width(), 0, size.width(), size.height()));
+}
- options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get()));
+void BalloonViewImpl::Deactivated() {
+ if (control_view_host_.get()) {
+ control_view_host_->Hide();
+ }
}
+////////////////////////////////////////////////////////////////////////////////
+// BalloonViewImpl private.
+
void BalloonViewImpl::DelayedClose(bool by_user) {
html_contents_->Shutdown();
html_contents_ = NULL;
balloon_->OnClose(by_user);
}
-bool BalloonViewImpl::IsFor(const Notification& notification) const {
- return balloon_->notification().IsSame(notification);
+void BalloonViewImpl::DenyPermission() {
+ DesktopNotificationService* service =
+ balloon_->profile()->GetDesktopNotificationService();
+ service->DenyPermission(balloon_->notification().origin_url());
+}
+
+gfx::NativeView BalloonViewImpl::GetParentNativeView() {
+ RenderWidgetHostView* view = html_contents_->render_view_host()->view();
+ DCHECK(view);
+ return view->GetNativeView();
}
} // namespace chromeos
diff --git a/chrome/browser/chromeos/notifications/balloon_view.h b/chrome/browser/chromeos/notifications/balloon_view.h
index 0ed77a8..c6032e6 100644
--- a/chrome/browser/chromeos/notifications/balloon_view.h
+++ b/chrome/browser/chromeos/notifications/balloon_view.h
@@ -7,7 +7,6 @@
#ifndef CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_BALLOON_VIEW_H_
#define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_BALLOON_VIEW_H_
-#include "app/menus/simple_menu_model.h"
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "base/task.h"
@@ -18,15 +17,14 @@
#include "gfx/point.h"
#include "gfx/rect.h"
#include "gfx/size.h"
-#include "views/controls/button/button.h"
-#include "views/controls/label.h"
-#include "views/controls/menu/view_menu_delegate.h"
#include "views/view.h"
namespace views {
class Menu2;
class MenuButton;
+class MouseEvent;
class TextButton;
+class WidgetGtk;
} // namespace views
class BalloonViewHost;
@@ -36,19 +34,19 @@ class NotificationSource;
namespace chromeos {
+class NotificationControlView;
+
// A balloon view is the UI component for a notification panel.
class BalloonViewImpl : public BalloonView,
public views::View,
- public views::ViewMenuDelegate,
- public menus::SimpleMenuModel::Delegate,
- public NotificationObserver,
- public views::ButtonListener {
+ public NotificationObserver {
public:
BalloonViewImpl(bool sticky, bool controls);
~BalloonViewImpl();
// views::View interface.
virtual void Layout();
+ virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child);
// BalloonView interface.
virtual void Show(Balloon* balloon);
@@ -67,22 +65,25 @@ class BalloonViewImpl : public BalloonView,
// True if the notification is sticky.
bool sticky() const { return sticky_; }
+ // True if the notification is being closed.
+ bool closed() const { return closed_; }
+
// True if the balloon is for the given |notification|.
bool IsFor(const Notification& notification) const;
- private:
- // views::ViewMenuDelegate interface.
- virtual void RunMenu(views::View* source, const gfx::Point& pt);
+ // Called when the notification becomes active (mouse is on).
+ void Activated();
- // views::ButtonListener interface.
- virtual void ButtonPressed(views::Button* sender, const views::Event&);
+ // Called when the notification becomes inactive.
+ void Deactivated();
- // menus::SimpleMenuModel::Delegate interface.
- virtual bool IsCommandIdChecked(int command_id) const;
- virtual bool IsCommandIdEnabled(int command_id) const;
- virtual bool GetAcceleratorForCommandId(int command_id,
- menus::Accelerator* accelerator);
- virtual void ExecuteCommand(int command_id);
+ private:
+ friend class NotificationControlView;
+
+ // views::View interface.
+ virtual gfx::Size GetPreferredSize() {
+ return gfx::Size(1000, 1000);
+ }
// NotificationObserver interface.
virtual void Observe(NotificationType type,
@@ -95,6 +96,12 @@ class BalloonViewImpl : public BalloonView,
// Do the delayed close work.
void DelayedClose(bool by_user);
+ // Denies the permission to show the ballooon from its source.
+ void DenyPermission();
+
+ // Returns the renderer's native view.
+ gfx::NativeView GetParentNativeView();
+
// Non-owned pointer to the balloon which owns this object.
Balloon* balloon_;
@@ -104,22 +111,17 @@ class BalloonViewImpl : public BalloonView,
// The following factory is used to call methods at a later time.
ScopedRunnableMethodFactory<BalloonViewImpl> method_factory_;
- // Pointer to sub-view is owned by the View sub-class.
- views::TextButton* close_button_;
-
- // Pointer to sub-view is owned by View class.
- views::Label* source_label_;
+ // A widget for ControlView.
+ scoped_ptr<views::WidgetGtk> control_view_host_;
- // The options menu.
- scoped_ptr<menus::SimpleMenuModel> options_menu_contents_;
- scoped_ptr<views::Menu2> options_menu_menu_;
- views::MenuButton* options_menu_button_;
bool stale_;
NotificationRegistrar notification_registrar_;
// A sticky flag. A sticky notification cannot be dismissed by a user.
bool sticky_;
// True if a notification should have info/option/dismiss label/buttons.
bool controls_;
+ // True if the notification is being closed.
+ bool closed_;
DISALLOW_COPY_AND_ASSIGN(BalloonViewImpl);
};
diff --git a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc
index f1018ca..424ea0e 100644
--- a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc
+++ b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc
@@ -16,6 +16,7 @@ class MockNotificationUI : public BalloonCollectionImpl::NotificationUI {
virtual void Remove(Balloon* balloon) {}
virtual void ResizeNotification(Balloon* balloon,
const gfx::Size& size) {}
+ virtual void SetActiveView(BalloonViewImpl* view) {}
};
MockBalloonCollection::MockBalloonCollection()
diff --git a/chrome/browser/chromeos/notifications/notification_browsertest.cc b/chrome/browser/chromeos/notifications/notification_browsertest.cc
index d90d1fd..916dabe 100644
--- a/chrome/browser/chromeos/notifications/notification_browsertest.cc
+++ b/chrome/browser/chromeos/notifications/notification_browsertest.cc
@@ -176,7 +176,7 @@ IN_PROC_BROWSER_TEST_F(NotificationTest, TestKeepSizeState) {
EXPECT_EQ(NotificationPanel::STICKY_AND_NEW, tester->state());
- panel->OnMouseMotion();
+ panel->OnMouseMotion(gfx::Point(10, 10));
EXPECT_EQ(NotificationPanel::KEEP_SIZE, tester->state());
collection->Remove(NewMockNotification("1"));
diff --git a/chrome/browser/chromeos/notifications/notification_panel.cc b/chrome/browser/chromeos/notifications/notification_panel.cc
index a673396..fbd56c5 100644
--- a/chrome/browser/chromeos/notifications/notification_panel.cc
+++ b/chrome/browser/chromeos/notifications/notification_panel.cc
@@ -8,6 +8,7 @@
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
+#include "chrome/browser/chromeos/notifications/balloon_collection_impl.h"
#include "chrome/browser/chromeos/notifications/balloon_view.h"
#include "gfx/canvas.h"
#include "grit/generated_resources.h"
@@ -62,10 +63,24 @@ class PanelWidget : public views::WidgetGtk {
panel_(panel) {
}
+ void UpdateControl() {
+ if (last_point_.get())
+ panel_->OnMouseMotion(*last_point_.get());
+ }
+
// views::WidgetGtk overrides.
virtual gboolean OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) {
gboolean result = WidgetGtk::OnMotionNotify(widget, event);
- panel_->OnMouseMotion();
+
+ int x = 0, y = 0;
+ GetContainedWidgetEventCoordinates(event, &x, &y);
+ if (!last_point_.get()) {
+ last_point_.reset(new gfx::Point(x, y));
+ } else {
+ last_point_->set_x(x);
+ last_point_->set_y(y);
+ }
+ panel_->OnMouseMotion(*last_point_.get());
return result;
}
@@ -78,12 +93,14 @@ class PanelWidget : public views::WidgetGtk {
GetBounds(&bounds, true);
if (!bounds.Contains(p)) {
panel_->OnMouseLeave();
+ last_point_.reset();
}
return result;
}
private:
chromeos::NotificationPanel* panel_;
+ scoped_ptr<gfx::Point> last_point_;
DISALLOW_COPY_AND_ASSIGN(PanelWidget);
};
@@ -176,6 +193,18 @@ class BalloonSubContainer : public views::View {
return NULL;
}
+ BalloonViewImpl* FindBalloonView(const gfx::Point point) {
+ gfx::Point copy(point);
+ ConvertPointToView(GetRootView(), this, &copy);
+ for (int i = GetChildViewCount() - 1; i >= 0; --i) {
+ views::View* view = GetChildViewAt(i);
+ if (view->bounds().Contains(copy)) {
+ return static_cast<BalloonViewImpl*>(view);
+ }
+ }
+ return NULL;
+ }
+
private:
gfx::Size preferred_size_;
int margin_;
@@ -252,10 +281,11 @@ class BalloonContainer : public views::View {
}
// Removes a ballon from the panel.
- void Remove(Balloon* balloon) {
+ BalloonViewImpl* Remove(Balloon* balloon) {
BalloonViewImpl* view =
static_cast<BalloonViewImpl*>(balloon->view());
GetContainerFor(balloon)->RemoveChildView(view);
+ return view;
}
// Returns the number of notifications added to the panel.
@@ -313,6 +343,11 @@ class BalloonContainer : public views::View {
return view ? view : non_sticky_container_->FindBalloonView(notification);
}
+ BalloonViewImpl* FindBalloonView(const gfx::Point& point) {
+ BalloonViewImpl* view = sticky_container_->FindBalloonView(point);
+ return view ? view : non_sticky_container_->FindBalloonView(point);
+ }
+
private:
BalloonSubContainer* GetContainerFor(Balloon* balloon) const {
BalloonViewImpl* view =
@@ -336,7 +371,8 @@ NotificationPanel::NotificationPanel()
state_(CLOSED),
task_factory_(this),
min_bounds_(0, 0, kBalloonMinWidth, kBalloonMinHeight),
- stale_timeout_(1000 * kStaleTimeoutInSeconds) {
+ stale_timeout_(1000 * kStaleTimeoutInSeconds),
+ active_(NULL) {
Init();
}
@@ -394,6 +430,7 @@ void NotificationPanel::Add(Balloon* balloon) {
// Don't resize the panel yet. The panel will be resized when WebKit tells
// the size in ResizeNotification.
UpdatePanel(false);
+ UpdateControl();
StartStaleTimer(balloon);
}
@@ -411,7 +448,11 @@ bool NotificationPanel::Update(Balloon* balloon) {
}
void NotificationPanel::Remove(Balloon* balloon) {
- balloon_container_->Remove(balloon);
+ BalloonViewImpl* view = balloon_container_->Remove(balloon);
+ if (view == active_) {
+ active_ = NULL;
+ }
+
// TODO(oshima): May be we shouldn't close
// if the mouse pointer is still on the panel.
if (balloon_container_->GetNotificationCount() == 0)
@@ -427,6 +468,7 @@ void NotificationPanel::Remove(Balloon* balloon) {
SET_STATE(MINIMIZED);
UpdatePanel(true);
}
+ UpdateControl();
}
void NotificationPanel::ResizeNotification(
@@ -442,6 +484,19 @@ void NotificationPanel::ResizeNotification(
UpdatePanel(true);
}
+void NotificationPanel::SetActiveView(BalloonViewImpl* view) {
+ // Don't change the active view if it's same notification,
+ // or the notification is being closed.
+ if (active_ == view || (view && view->closed())) {
+ return;
+ }
+ if (active_)
+ active_->Deactivated();
+ active_ = view;
+ if (active_)
+ active_->Activated();
+}
+
////////////////////////////////////////////////////////////////////////////////
// PanelController overrides.
@@ -492,12 +547,15 @@ void NotificationPanel::Observe(NotificationType type,
// PanelController public.
void NotificationPanel::OnMouseLeave() {
+ SetActiveView(NULL);
if (balloon_container_->GetNotificationCount() == 0)
SET_STATE(CLOSED);
UpdatePanel(true);
}
-void NotificationPanel::OnMouseMotion() {
+void NotificationPanel::OnMouseMotion(const gfx::Point& point) {
+ SetActiveView(balloon_container_->FindBalloonView(point));
+
SET_STATE(KEEP_SIZE);
}
@@ -568,6 +626,11 @@ void NotificationPanel::UpdatePanel(bool contents_changed) {
}
}
+void NotificationPanel::UpdateControl() {
+ if (panel_widget_.get())
+ static_cast<PanelWidget*>(panel_widget_.get())->UpdateControl();
+}
+
gfx::Rect NotificationPanel::GetPreferredBounds() {
gfx::Size pref_size = balloon_container_->GetPreferredSize();
int new_height = std::min(pref_size.height(), kMaxPanelHeight);
diff --git a/chrome/browser/chromeos/notifications/notification_panel.h b/chrome/browser/chromeos/notifications/notification_panel.h
index c739880..dfc76a5 100644
--- a/chrome/browser/chromeos/notifications/notification_panel.h
+++ b/chrome/browser/chromeos/notifications/notification_panel.h
@@ -93,6 +93,7 @@ class NotificationPanel : public PanelController::Delegate,
virtual void Remove(Balloon* balloon);
virtual void ResizeNotification(Balloon* balloon,
const gfx::Size& size);
+ virtual void SetActiveView(BalloonViewImpl* view);
// PanelController overrides.
virtual string16 GetPanelTitle();
@@ -106,7 +107,7 @@ class NotificationPanel : public PanelController::Delegate,
// Called when a mouse left the panel window.
void OnMouseLeave();
- void OnMouseMotion();
+ void OnMouseMotion(const gfx::Point& point);
NotificationPanelTester* GetTester();
@@ -121,6 +122,9 @@ class NotificationPanel : public PanelController::Delegate,
// Update the Panel Size according to its state.
void UpdatePanel(bool contents_changed);
+ // Update the notification's control view state.
+ void UpdateControl();
+
// Returns the panel's preferred bounds in the screen's coordinates.
// The position will be controlled by window manager so
// the origin is always (0, 0).
@@ -141,17 +145,41 @@ class NotificationPanel : public PanelController::Delegate,
// Mark the given notification as stale.
void MarkStale(const Notification& notification);
+ // Contains all notifications.
BalloonContainer* balloon_container_;
+
+ // The notification panel's widget.
scoped_ptr<views::Widget> panel_widget_;
+
+ // Panel controller for the notification panel.
scoped_ptr<PanelController> panel_controller_;
+
+ // A scrollable parent of the BalloonContainer.
+ // This is owned by the panel so that we can re-attache to the widget
+ // when closing and opening the panel.
scoped_ptr<views::ScrollView> scroll_view_;
+
+ // Panel's state.
State state_;
+
ScopedRunnableMethodFactory<NotificationPanel> task_factory_;
+
+ // The minimum size of a notification.
gfx::Rect min_bounds_;
- scoped_ptr<NotificationPanelTester> tester_;
+
+ // Stale timeout.
int stale_timeout_;
+
+ // A registrar to subscribe PANEL_STATE_CHANGED event.
NotificationRegistrar registrar_;
+ // The notification a mouse pointer is currently on. NULL if the mouse
+ // is out of the panel.
+ BalloonViewImpl* active_;
+
+ // An object that provides interfacce for tests.
+ scoped_ptr<NotificationPanelTester> tester_;
+
DISALLOW_COPY_AND_ASSIGN(NotificationPanel);
};