summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-12 21:04:39 +0000
committeroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-12 21:04:39 +0000
commit3a9c26f147ab16179197bd4460f4ec6c476afcf7 (patch)
treeaa850e93e76b3549550ae5d4d1e2bccbd07083b1
parente512583b79e366db399a6566964412bb6e5af823 (diff)
downloadchromium_src-3a9c26f147ab16179197bd4460f4ec6c476afcf7.zip
chromium_src-3a9c26f147ab16179197bd4460f4ec6c476afcf7.tar.gz
chromium_src-3a9c26f147ab16179197bd4460f4ec6c476afcf7.tar.bz2
Autoslide/hide notifications after timeout.
* Introduced panel's state to manage which notifications will be shown.' * Added "OnPanelStateChanged" to PanelController::Delegate method to monitor the panel's state. * Removed unnecessary SetSize in BalloonViewImpl. The size is set in BalloonViewImpl::Layout. BUG=33306 TEST=This requires chromeos's wm and system notification to test (which does not exit yet). None for now but I am writing unittest now. Review URL: http://codereview.chromium.org/874004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41484 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/frame/panel_browser_view.h1
-rw-r--r--chrome/browser/chromeos/frame/panel_controller.cc19
-rw-r--r--chrome/browser/chromeos/frame/panel_controller.h10
-rw-r--r--chrome/browser/chromeos/notifications/balloon_collection_impl.cc4
-rw-r--r--chrome/browser/chromeos/notifications/balloon_view.cc7
-rw-r--r--chrome/browser/chromeos/notifications/balloon_view.h12
-rw-r--r--chrome/browser/chromeos/notifications/notification_panel.cc195
-rw-r--r--chrome/browser/chromeos/notifications/notification_panel.h62
-rw-r--r--chrome/browser/views/notifications/balloon_view_host.cc1
-rw-r--r--views/view.cc6
-rw-r--r--views/view.h3
11 files changed, 295 insertions, 25 deletions
diff --git a/chrome/browser/chromeos/frame/panel_browser_view.h b/chrome/browser/chromeos/frame/panel_browser_view.h
index 0bd7c6f8..5e8b770 100644
--- a/chrome/browser/chromeos/frame/panel_browser_view.h
+++ b/chrome/browser/chromeos/frame/panel_browser_view.h
@@ -32,6 +32,7 @@ class PanelBrowserView : public BrowserView,
virtual string16 GetPanelTitle();
virtual SkBitmap GetPanelIcon();
virtual void ClosePanel();
+ virtual void OnPanelStateChanged(PanelController::State state) {}
private:
// Controls interactions with the window manager for popup panels.
diff --git a/chrome/browser/chromeos/frame/panel_controller.cc b/chrome/browser/chromeos/frame/panel_controller.cc
index 8f2d1b7..4008342 100644
--- a/chrome/browser/chromeos/frame/panel_controller.cc
+++ b/chrome/browser/chromeos/frame/panel_controller.cc
@@ -166,10 +166,8 @@ void PanelController::TitleMouseReleased(
mouse_down_ = false;
if (!dragging_) {
- WmIpc::Message msg(WmIpc::Message::WM_SET_PANEL_STATE);
- msg.set_param(0, panel_xid_);
- msg.set_param(1, expanded_ ? 0 : 1);
- WmIpc::instance()->SendMessage(msg);
+ SetState(expanded_ ?
+ PanelController::MINIMIZED : PanelController::EXPANDED);
} else {
WmIpc::Message msg(WmIpc::Message::WM_NOTIFY_PANEL_DRAG_COMPLETE);
msg.set_param(0, panel_xid_);
@@ -178,6 +176,13 @@ void PanelController::TitleMouseReleased(
}
}
+void PanelController::SetState(State state) {
+ WmIpc::Message msg(WmIpc::Message::WM_SET_PANEL_STATE);
+ msg.set_param(0, panel_xid_);
+ msg.set_param(1, state == EXPANDED);
+ WmIpc::instance()->SendMessage(msg);
+}
+
bool PanelController::TitleMouseDragged(const views::MouseEvent& event) {
if (!mouse_down_) {
return false;
@@ -230,7 +235,11 @@ bool PanelController::PanelClientEvent(GdkEventClient* event) {
WmIpc::Message msg;
WmIpc::instance()->DecodeMessage(*event, &msg);
if (msg.type() == WmIpc::Message::CHROME_NOTIFY_PANEL_STATE) {
- expanded_ = msg.param(0);
+ bool new_state = msg.param(0);
+ if (expanded_ != new_state) {
+ expanded_ = new_state;
+ delegate_->OnPanelStateChanged(new_state ? EXPANDED : MINIMIZED);
+ }
}
return true;
}
diff --git a/chrome/browser/chromeos/frame/panel_controller.h b/chrome/browser/chromeos/frame/panel_controller.h
index cac3ed0..5a78569 100644
--- a/chrome/browser/chromeos/frame/panel_controller.h
+++ b/chrome/browser/chromeos/frame/panel_controller.h
@@ -26,6 +26,11 @@ namespace chromeos {
// Controls interactions with the WM for popups / panels.
class PanelController : public views::ButtonListener {
public:
+ enum State {
+ EXPANDED,
+ MINIMIZED,
+ };
+
// Delegate to control panel's appearance and behavior.
class Delegate {
public:
@@ -37,6 +42,8 @@ class PanelController : public views::ButtonListener {
// Close the panel. Called when a close button is pressed.
virtual void ClosePanel() = 0;
+
+ virtual void OnPanelStateChanged(State state) = 0;
};
PanelController(Delegate* delegate_window,
@@ -53,6 +60,9 @@ class PanelController : public views::ButtonListener {
void UpdateTitleBar();
void Close();
+
+ void SetState(State state);
+
// ButtonListener methods.
virtual void ButtonPressed(views::Button* sender, const views::Event& event);
diff --git a/chrome/browser/chromeos/notifications/balloon_collection_impl.cc b/chrome/browser/chromeos/notifications/balloon_collection_impl.cc
index 590e430..d4e073c 100644
--- a/chrome/browser/chromeos/notifications/balloon_collection_impl.cc
+++ b/chrome/browser/chromeos/notifications/balloon_collection_impl.cc
@@ -36,6 +36,10 @@ BalloonCollectionImpl::BalloonCollectionImpl()
}
BalloonCollectionImpl::~BalloonCollectionImpl() {
+ // We need to remove the panel first because deleting
+ // views that are not owned by parent will not remove
+ // themselves from the parent.
+ panel_.reset();
STLDeleteElements(&balloons_);
}
diff --git a/chrome/browser/chromeos/notifications/balloon_view.cc b/chrome/browser/chromeos/notifications/balloon_view.cc
index ae4c8f5..4d33e02 100644
--- a/chrome/browser/chromeos/notifications/balloon_view.cc
+++ b/chrome/browser/chromeos/notifications/balloon_view.cc
@@ -42,13 +42,17 @@ BalloonViewImpl::BalloonViewImpl()
close_button_(NULL),
options_menu_contents_(NULL),
options_menu_menu_(NULL),
- options_menu_button_(NULL) {
+ options_menu_button_(NULL),
+ stale_(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 (html_contents_) {
+ html_contents_->Shutdown();
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -229,6 +233,7 @@ void BalloonViewImpl::CreateOptionsMenu() {
void BalloonViewImpl::DelayedClose(bool by_user) {
html_contents_->Shutdown();
+ html_contents_ = NULL;
balloon_->OnClose(by_user);
}
diff --git a/chrome/browser/chromeos/notifications/balloon_view.h b/chrome/browser/chromeos/notifications/balloon_view.h
index ff3fcc4..9eb5378 100644
--- a/chrome/browser/chromeos/notifications/balloon_view.h
+++ b/chrome/browser/chromeos/notifications/balloon_view.h
@@ -55,6 +55,16 @@ class BalloonViewImpl : public BalloonView,
void RepositionToBalloon();
gfx::Size GetSize() const;
+ // True if the notification is stale. False if the notification is new.
+ bool stale() {
+ return stale_;
+ }
+
+ // Makes the notification stable.
+ void make_stale() {
+ stale_ = true;
+ }
+
private:
// views::View interface.
virtual gfx::Size GetPreferredSize() {
@@ -104,7 +114,7 @@ class BalloonViewImpl : public BalloonView,
scoped_ptr<menus::SimpleMenuModel> options_menu_contents_;
scoped_ptr<views::Menu2> options_menu_menu_;
views::MenuButton* options_menu_button_;
-
+ bool stale_;
NotificationRegistrar notification_registrar_;
DISALLOW_COPY_AND_ASSIGN(BalloonViewImpl);
diff --git a/chrome/browser/chromeos/notifications/notification_panel.cc b/chrome/browser/chromeos/notifications/notification_panel.cc
index 9f980fe..f16f690 100644
--- a/chrome/browser/chromeos/notifications/notification_panel.cc
+++ b/chrome/browser/chromeos/notifications/notification_panel.cc
@@ -13,6 +13,7 @@
#include "grit/generated_resources.h"
#include "views/background.h"
#include "views/controls/scroll_view.h"
+#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"
namespace {
@@ -26,6 +27,11 @@ const int kBalloonMaxHeight = 120;
// TODO(oshima): Get this from system's metrics.
const int kMaxPanelHeight = 400;
+// The duration for a new notification to become stale.
+const int kStaleTimeoutInSeconds = 10;
+
+using chromeos::BalloonViewImpl;
+
class BalloonSubContainer : public views::View {
public:
explicit BalloonSubContainer(int margin)
@@ -67,6 +73,44 @@ class BalloonSubContainer : public views::View {
SizeToPreferredSize();
}
+ // Returns the bounds that covers new notifications.
+ gfx::Rect GetNewBounds() {
+ gfx::Rect rect;
+ for (int i = GetChildViewCount() - 1; i >= 0; --i) {
+ BalloonViewImpl* view =
+ static_cast<BalloonViewImpl*>(GetChildViewAt(i));
+ if (!view->stale()) {
+ if (rect.IsEmpty()) {
+ rect = view->bounds();
+ } else {
+ rect = rect.Union(bounds());
+ }
+ }
+ }
+ return gfx::Rect(x(), y(), rect.width(), rect.height());
+ }
+
+ // Returns # of new notifications.
+ int GetNewCount() {
+ int count = 0;
+ for (int i = GetChildViewCount() - 1; i >= 0; --i) {
+ BalloonViewImpl* view =
+ static_cast<BalloonViewImpl*>(GetChildViewAt(i));
+ if (!view->stale())
+ count++;
+ }
+ return count;
+ }
+
+ // Make all notifications stale.
+ void MakeAllStale() {
+ for (int i = GetChildViewCount() - 1; i >= 0; --i) {
+ BalloonViewImpl* view =
+ static_cast<BalloonViewImpl*>(GetChildViewAt(i));
+ view->make_stale();
+ }
+ }
+
private:
gfx::Size preferred_size_;
int margin_;
@@ -105,6 +149,17 @@ class BalloonContainer : public views::View {
return preferred_size_;
}
+ // Returns the size that covers sticky and new notifications.
+ gfx::Size GetStickyNewSize() {
+ gfx::Rect new_sticky = sticky_container_->bounds();
+ gfx::Rect new_non_sticky = non_sticky_container_->GetNewBounds();
+ if (new_sticky.IsEmpty())
+ return new_non_sticky.size();
+ if (new_non_sticky.IsEmpty())
+ return new_sticky.size();
+ return new_sticky.Union(new_non_sticky).size();
+ }
+
// Add a ballon to the panel.
void Add(Balloon* balloon) {
BalloonViewImpl* view =
@@ -125,6 +180,17 @@ class BalloonContainer : public views::View {
non_sticky_container_->GetChildViewCount();
}
+ // Returns true if the container has sticky notification.
+ bool HasStickyNotifications() {
+ return sticky_container_->GetChildViewCount() > 0;
+ }
+
+ // Returns true if the |view| is contained in the panel.
+ bool HasBalloonView(View* view) {
+ return sticky_container_->HasChildView(view) ||
+ non_sticky_container_->HasChildView(view);
+ }
+
// Update the bounds so that all notifications are visible.
void UpdateBounds() {
sticky_container_->UpdateBounds();
@@ -141,6 +207,11 @@ class BalloonContainer : public views::View {
SizeToPreferredSize();
}
+ void MakeAllStale() {
+ sticky_container_->MakeAllStale();
+ non_sticky_container_->MakeAllStale();
+ }
+
private:
BalloonSubContainer* GetContainerFor(Balloon* balloon) const {
return balloon->notification().sticky() ?
@@ -158,11 +229,14 @@ class BalloonContainer : public views::View {
};
NotificationPanel::NotificationPanel()
- : balloon_container_(NULL) {
+ : balloon_container_(NULL),
+ state_(CLOSED),
+ task_factory_(this) {
Init();
}
NotificationPanel::~NotificationPanel() {
+ Hide();
}
////////////////////////////////////////////////////////////////////////////////
@@ -173,18 +247,33 @@ void NotificationPanel::Show() {
// TODO(oshima): Using window because Popup widget behaves weird
// when resizing. This needs to be investigated.
panel_widget_.reset(new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW));
- panel_widget_->Init(NULL, GetPreferredBounds());
+ gfx::Rect bounds = GetPreferredBounds();
+ if (bounds.width() < kBalloonMinWidth ||
+ bounds.height() < kBalloonMinHeight) {
+ // Gtk uses its own default size when the size is empty.
+ // Use the minimum size as a default.
+ bounds.SetRect(0, 0, kBalloonMinWidth, kBalloonMinHeight);
+ }
+ panel_widget_->Init(NULL, bounds);
+ // TODO(oshima): I needed the following code in order to get sizing
+ // reliably. Investigate and fix it in WidgetGtk.
+ gtk_widget_set_size_request(GTK_WIDGET(panel_widget_->GetNativeView()),
+ bounds.width(), bounds.height());
panel_widget_->SetContentsView(scroll_view_.get());
panel_controller_.reset(
new PanelController(this,
GTK_WINDOW(panel_widget_->GetNativeView()),
- gfx::Rect(0, 0, 1000, 1000)));
+ gfx::Rect(0, 0, kBalloonMinWidth, 1)));
}
panel_widget_->Show();
}
void NotificationPanel::Hide() {
if (panel_widget_.get()) {
+ // We need to remove & detach the scroll view from hierarchy to
+ // avoid GTK deleting child.
+ // TODO(oshima): handle this details in WidgetGtk.
+ panel_widget_->GetRootView()->RemoveChildView(scroll_view_.get());
panel_widget_.release()->Close();
panel_controller_.release()->Close();
}
@@ -195,13 +284,19 @@ void NotificationPanel::Hide() {
void NotificationPanel::Add(Balloon* balloon) {
balloon_container_->Add(balloon);
- UpdateSize();
+ if (state_ == CLOSED || state_ == MINIMIZED)
+ state_ = STICKY_AND_NEW;
Show();
+ UpdatePanel(true);
+ StartStaleTimer(balloon);
}
void NotificationPanel::Remove(Balloon* balloon) {
balloon_container_->Remove(balloon);
- UpdateSize();
+ // no change to the state
+ if (balloon_container_->GetNotificationCount() == 0)
+ state_ = CLOSED;
+ UpdatePanel(true);
}
void NotificationPanel::ResizeNotification(
@@ -214,7 +309,7 @@ void NotificationPanel::ResizeNotification(
std::min(kBalloonMaxHeight, size.height())));
balloon->set_content_size(real_size);
static_cast<BalloonViewImpl*>(balloon->view())->Layout();
- UpdateSize();
+ UpdatePanel(true);
}
////////////////////////////////////////////////////////////////////////////////
@@ -229,7 +324,28 @@ SkBitmap NotificationPanel::GetPanelIcon() {
}
void NotificationPanel::ClosePanel() {
- panel_widget_.release()->Close();
+ state_ = CLOSED;
+ UpdatePanel(false);
+}
+
+void NotificationPanel::OnPanelStateChanged(PanelController::State state) {
+ switch (state) {
+ case PanelController::EXPANDED:
+ // Geting expanded in STICKY_AND_NEW state means that a new
+ // notification is added, so just leave the state. Otherwise,
+ // expand to full.
+ if (state_ != STICKY_AND_NEW)
+ state_ = FULL;
+ // When the panel is to be expanded, we either show all, or
+ // show only sticky/new, depending on the state.
+ UpdatePanel(false);
+ break;
+ case PanelController::MINIMIZED:
+ state_ = MINIMIZED;
+ // Make all notifications stale when a user minimize the panel.
+ balloon_container_->MakeAllStale();
+ break;
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -246,14 +362,33 @@ void NotificationPanel::Init() {
scroll_view_->SetContents(balloon_container_);
}
-void NotificationPanel::UpdateSize() {
- balloon_container_->UpdateBounds();
- scroll_view_->Layout();
- if (panel_widget_.get()) {
- if (balloon_container_->GetNotificationCount() == 0)
+void NotificationPanel::UpdatePanel(bool contents_changed) {
+ if (contents_changed) {
+ balloon_container_->UpdateBounds();
+ scroll_view_->Layout();
+ }
+ switch(state_) {
+ case CLOSED:
+ balloon_container_->MakeAllStale();
Hide();
- else
- panel_widget_->SetBounds(GetPreferredBounds());
+ break;
+ case MINIMIZED:
+ balloon_container_->MakeAllStale();
+ if (panel_controller_.get())
+ panel_controller_->SetState(PanelController::MINIMIZED);
+ break;
+ case FULL:
+ if (panel_widget_.get()) {
+ panel_widget_->SetBounds(GetPreferredBounds());
+ panel_controller_->SetState(PanelController::EXPANDED);
+ }
+ break;
+ case STICKY_AND_NEW:
+ if (panel_widget_.get()) {
+ panel_widget_->SetBounds(GetStickyNewBounds());
+ panel_controller_->SetState(PanelController::EXPANDED);
+ }
+ break;
}
}
@@ -267,4 +402,36 @@ gfx::Rect NotificationPanel::GetPreferredBounds() {
return gfx::Rect(0, 0, new_width, new_height);
}
+gfx::Rect NotificationPanel::GetStickyNewBounds() {
+ gfx::Size pref_size = balloon_container_->GetPreferredSize();
+ gfx::Size sticky_size = balloon_container_->GetStickyNewSize();
+ int new_height = std::min(sticky_size.height(), kMaxPanelHeight);
+ int new_width = pref_size.width();
+ // Adjust the width to avoid showing a horizontal scroll bar.
+ if (new_height != pref_size.height())
+ new_width += scroll_view_->GetScrollBarWidth();
+ return gfx::Rect(0, 0, new_width, new_height);
+}
+
+void NotificationPanel::StartStaleTimer(Balloon* balloon) {
+ BalloonViewImpl* view = static_cast<BalloonViewImpl*>(balloon->view());
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ task_factory_.NewRunnableMethod(
+ &NotificationPanel::StaleNotification, view),
+ 1000 * kStaleTimeoutInSeconds);
+}
+
+void NotificationPanel::StaleNotification(BalloonViewImpl* view) {
+ if (balloon_container_->HasBalloonView(view) && !view->stale()) {
+ view->make_stale();
+ if (balloon_container_->HasStickyNotifications()) {
+ state_ = STICKY_AND_NEW;
+ } else {
+ state_ = MINIMIZED;
+ }
+ UpdatePanel(false);
+ }
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/notifications/notification_panel.h b/chrome/browser/chromeos/notifications/notification_panel.h
index 1bdddc9..3036b18 100644
--- a/chrome/browser/chromeos/notifications/notification_panel.h
+++ b/chrome/browser/chromeos/notifications/notification_panel.h
@@ -8,6 +8,7 @@
#define CHROME_BROWSER_CHROMEOS_NOTIFICATIONS_NOTIFICATION_PANEL_H_
#include "base/gfx/rect.h"
+#include "base/task.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/chromeos/frame/panel_controller.h"
#include "chrome/browser/chromeos/notifications/balloon_collection_impl.h"
@@ -21,7 +22,43 @@ class ScrollView;
namespace chromeos {
class BalloonContainer;
-
+class BalloonViewImpl;
+
+// NotificationPanel is a panel that displays notifications. It has
+// several states and displays the different portion of notifications
+// depending on in which state the panel is. The following shows
+// how the panel's state changes in response to various events.
+//
+// Event List:
+// close: a user pressed close button on the title bar,
+// or the system closed the panel.
+// new : a new notification is added.
+// stale: one of new notifications became stale.
+// expand: a user pressed minimized panel to expand.
+// minimize: a user pressed the panel's title bar to minimize.
+// For state, see State enum's description below.
+//
+// CLOSE<--(event=close)-+ +--(event=stale, cond=has new|sticky)
+// | | | (event=new)
+// | | V |
+// +--(event=new)--> STICKY_AND_NEW----------+
+// | | ^
+// | (event=stale, |
+// | cond=has new, no sticy) (event=new)
+// | (event=minimize) |
+// | | |
+// | V |
+// | MINIMIZED ---(event=close)--> [CLOSE]
+// | | ^
+// | | |
+// | (event=expand) (event=minmize)
+// | V |
+// +--(event=open)----> FULL --(event=stale)(event=new)
+// | | |
+// (event=close) +--------------+
+// |
+// [CLOSE] <------+
+//
class NotificationPanel : public PanelController::Delegate,
public BalloonCollectionImpl::NotificationUI {
public:
@@ -42,22 +79,41 @@ class NotificationPanel : public PanelController::Delegate,
virtual string16 GetPanelTitle();
virtual SkBitmap GetPanelIcon();
virtual void ClosePanel();
+ virtual void OnPanelStateChanged(PanelController::State state);
private:
+ enum State {
+ FULL, // Show all notifications
+ STICKY_AND_NEW, // Show only new and sticky notifications.
+ MINIMIZED, // The panel is minimized.
+ CLOSED, // The panel is closed.
+ };
+
void Init();
- // Update the Panel Size to the preferred size.
- void UpdateSize();
+ // Update the Panel Size according to its state.
+ void UpdatePanel(bool contents_changed);
// 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).
gfx::Rect GetPreferredBounds();
+ // Returns the bounds that covers sticky and new notifications.
+ gfx::Rect GetStickyNewBounds();
+
+ void StartStaleTimer(Balloon* balloon);
+
+ // A callback function that is called when the notification
+ // (that the view is associated with) becomes stale after a timeout.
+ void StaleNotification(BalloonViewImpl* view);
+
BalloonContainer* balloon_container_;
scoped_ptr<views::Widget> panel_widget_;
scoped_ptr<PanelController> panel_controller_;
scoped_ptr<views::ScrollView> scroll_view_;
+ State state_;
+ ScopedRunnableMethodFactory<NotificationPanel> task_factory_;
DISALLOW_COPY_AND_ASSIGN(NotificationPanel);
};
diff --git a/chrome/browser/views/notifications/balloon_view_host.cc b/chrome/browser/views/notifications/balloon_view_host.cc
index 94f645a..ea8d55d 100644
--- a/chrome/browser/views/notifications/balloon_view_host.cc
+++ b/chrome/browser/views/notifications/balloon_view_host.cc
@@ -154,7 +154,6 @@ void BalloonViewHost::Init(gfx::NativeView parent_hwnd) {
static_cast<RenderWidgetHostViewGtk*>(view);
view_gtk->InitAsChild();
Attach(view_gtk->native_view());
- view->SetSize(gfx::Size(width(), height()));
#else
NOTIMPLEMENTED();
#endif
diff --git a/views/view.cc b/views/view.cc
index c04a4df7..437b3b7 100644
--- a/views/view.cc
+++ b/views/view.cc
@@ -556,6 +556,12 @@ int View::GetChildViewCount() const {
return static_cast<int>(child_views_.size());
}
+bool View::HasChildView(View* a_view) {
+ return find(child_views_.begin(),
+ child_views_.end(),
+ a_view) != child_views_.end();
+}
+
void View::RemoveChildView(View* a_view) {
DoRemoveChildView(a_view, true, true, false);
}
diff --git a/views/view.h b/views/view.h
index d493a0b..0dcc734 100644
--- a/views/view.h
+++ b/views/view.h
@@ -435,6 +435,9 @@ class View : public AcceleratorTarget {
// Get the number of child Views.
int GetChildViewCount() const;
+ // Tests if this view has a given view as direct child.
+ bool HasChildView(View* a_view);
+
// Returns the deepest descendant that contains the specified point.
virtual View* GetViewForPoint(const gfx::Point& point);