summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-15 15:36:03 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-15 15:36:03 +0000
commit3da0bfd1d9974b55d3358aad5954836e9986ef4b (patch)
treefbe33a78ca5f6aba01b5544516c66c17953d6552
parenta20720983ddc7796ba3b93b3e57be2e5ba7f31a5 (diff)
downloadchromium_src-3da0bfd1d9974b55d3358aad5954836e9986ef4b.zip
chromium_src-3da0bfd1d9974b55d3358aad5954836e9986ef4b.tar.gz
chromium_src-3da0bfd1d9974b55d3358aad5954836e9986ef4b.tar.bz2
Changes the tab close button to a dot, unless you're near the button
or the tab is selected. I'm not to keen on the mouse near names, if you have better ideas please say so. BUG=45743 TEST=none Review URL: http://codereview.chromium.org/2796006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49795 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/views/tabs/base_tab.cc98
-rw-r--r--chrome/browser/views/tabs/base_tab.h14
-rw-r--r--chrome/browser/views/tabs/side_tab.cc8
-rw-r--r--chrome/browser/views/tabs/tab.cc26
-rw-r--r--chrome/browser/views/tabs/tab_strip.cc9
-rw-r--r--views/controls/button/image_button.h19
-rw-r--r--views/event.h6
-rw-r--r--views/view.cc33
-rw-r--r--views/view.h30
-rw-r--r--views/widget/root_view.cc71
-rw-r--r--views/widget/root_view.h24
11 files changed, 289 insertions, 49 deletions
diff --git a/chrome/browser/views/tabs/base_tab.cc b/chrome/browser/views/tabs/base_tab.cc
index f128abc..04124ed 100644
--- a/chrome/browser/views/tabs/base_tab.cc
+++ b/chrome/browser/views/tabs/base_tab.cc
@@ -20,6 +20,7 @@
#include "gfx/canvas.h"
#include "gfx/favicon_size.h"
#include "gfx/font.h"
+#include "gfx/skbitmap_operations.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
@@ -48,20 +49,36 @@ static SkBitmap* close_button_p = NULL;
static SkBitmap* crashed_fav_icon = NULL;
-namespace {
-
////////////////////////////////////////////////////////////////////////////////
// TabCloseButton
//
// This is a Button subclass that causes middle clicks to be forwarded to the
// parent View by explicitly not handling them in OnMousePressed.
-class TabCloseButton : public views::ImageButton {
+class BaseTab::TabCloseButton : public views::ImageButton {
public:
- explicit TabCloseButton(views::ButtonListener* listener)
- : views::ImageButton(listener) {
+ TabCloseButton(BaseTab* tab, bool show_mini_dot)
+ : views::ImageButton(tab),
+ tab_(tab),
+ is_mouse_near_(!show_mini_dot),
+ color_(0) {
}
virtual ~TabCloseButton() {}
+ // Sets the color used to derive the background color.
+ void SetColor(SkColor color) {
+ if (color_ == color)
+ return;
+
+ color_ = color;
+ // This is invoked from Paint, so we don't need to schedule a paint.
+ UpdateBackgroundImage(false);
+ }
+
+ // Invoked when the selected state changes.
+ void SelectedStateChanged() {
+ UpdateBackgroundImage(true);
+ }
+
virtual bool OnMousePressed(const views::MouseEvent& event) {
bool handled = ImageButton::OnMousePressed(event);
// Explicitly mark midle-mouse clicks as non-handled to ensure the tab
@@ -82,12 +99,63 @@ class TabCloseButton : public views::ImageButton {
GetParent()->OnMouseExited(event);
}
+ virtual void OnMouseNear(const views::MouseEvent& event) {
+ is_mouse_near_ = true;
+ UpdateBackgroundImage(true);
+ }
+
+ virtual void OnMouseExitedNear(const views::MouseEvent& event) {
+ is_mouse_near_ = false;
+ UpdateBackgroundImage(true);
+ }
+
private:
+ // Returns the image to use for the background.
+ static const SkBitmap& GetBackgroundImage(SkColor color, bool is_mouse_near) {
+ // All tabs share the same color, so we cache the bitmaps.
+ static SkColor cached_color = 0;
+ static SkBitmap* near_image = NULL;
+ static SkBitmap* far_image = NULL;
+ if (!near_image || cached_color != color) {
+ cached_color = color;
+ if (!near_image) {
+ near_image = new SkBitmap();
+ far_image = new SkBitmap();
+ }
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ *near_image = SkBitmapOperations::CreateButtonBackground(
+ color,
+ *rb.GetBitmapNamed(IDR_TAB_CLOSE),
+ *rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK));
+ *far_image = SkBitmapOperations::CreateButtonBackground(
+ color,
+ *rb.GetBitmapNamed(IDR_TAB_CLOSE),
+ *rb.GetBitmapNamed(IDR_TAB_CLOSE_DOT_MASK));
+ }
+ return is_mouse_near ? *near_image : *far_image;
+ }
+
+ // Should we use the near image?
+ bool use_near_image() const { return is_mouse_near_ || tab_->IsSelected(); }
+
+ // Resets the background image.
+ void UpdateBackgroundImage(bool paint) {
+ set_background_image(GetBackgroundImage(color_, use_near_image()));
+ if (paint)
+ SchedulePaint();
+ }
+
+ BaseTab* tab_;
+
+ // Is the mouse near the close button?
+ bool is_mouse_near_;
+
+ // Color used to derive the background image from.
+ SkColor color_;
+
DISALLOW_COPY_AND_ASSIGN(TabCloseButton);
};
-} // namespace
-
// static
int BaseTab::close_button_width_ = 0;
// static
@@ -139,7 +207,7 @@ class BaseTab::FavIconCrashAnimation : public LinearAnimation,
DISALLOW_COPY_AND_ASSIGN(FavIconCrashAnimation);
};
-BaseTab::BaseTab(TabController* controller)
+BaseTab::BaseTab(TabController* controller, bool show_mini_dot)
: controller_(controller),
closing_(false),
dragging_(false),
@@ -151,7 +219,7 @@ BaseTab::BaseTab(TabController* controller)
BaseTab::InitResources();
// Add the Close Button.
- TabCloseButton* close_button = new TabCloseButton(this);
+ TabCloseButton* close_button = new TabCloseButton(this, show_mini_dot);
close_button_ = close_button;
close_button->SetImage(views::CustomButton::BS_NORMAL, close_button_n);
close_button->SetImage(views::CustomButton::BS_HOT, close_button_h);
@@ -228,6 +296,10 @@ void BaseTab::StopPulse() {
pulse_animation_.reset(NULL);
}
+void BaseTab::SelectedChanged() {
+ close_button_->SelectedStateChanged();
+}
+
bool BaseTab::IsSelected() const {
return controller() ? controller()->IsTabSelected(this) : true;
}
@@ -334,6 +406,14 @@ void BaseTab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state,
SchedulePaint();
}
+views::ImageButton* BaseTab::close_button() const {
+ return close_button_;
+}
+
+void BaseTab::SetCloseButtonColor(SkColor color) {
+ close_button_->SetColor(color);
+}
+
void BaseTab::PaintIcon(gfx::Canvas* canvas, int x, int y) {
if (base::i18n::IsRTL()) {
if (!data().favicon.isNull())
diff --git a/chrome/browser/views/tabs/base_tab.h b/chrome/browser/views/tabs/base_tab.h
index e2ac34a..c0c9bde 100644
--- a/chrome/browser/views/tabs/base_tab.h
+++ b/chrome/browser/views/tabs/base_tab.h
@@ -32,7 +32,7 @@ class BaseTab : public AnimationDelegate,
public views::ContextMenuController,
public views::View {
public:
- explicit BaseTab(TabController* controller);
+ BaseTab(TabController* controller, bool show_mini_dot);
~BaseTab();
// Sets the data this tabs displays. Invokes DataChanged for subclasses to
@@ -71,6 +71,10 @@ class BaseTab : public AnimationDelegate,
theme_provider_ = provider;
}
+ // Invoked when the selected state changes. Resets the image of the close
+ // button.
+ void SelectedChanged();
+
// Returns true if the tab is selected.
virtual bool IsSelected() const;
@@ -104,7 +108,10 @@ class BaseTab : public AnimationDelegate,
return hover_animation_.get();
}
- views::ImageButton* close_button() const { return close_button_; }
+ views::ImageButton* close_button() const;
+
+ // Sets the color used to derive the close button images.
+ void SetCloseButtonColor(SkColor color);
// Paints the icon at the specified x-coordinate.
void PaintIcon(gfx::Canvas* canvas, int x, int y);
@@ -143,6 +150,7 @@ class BaseTab : public AnimationDelegate,
private:
// The animation object used to swap the favicon with the sad tab icon.
class FavIconCrashAnimation;
+ class TabCloseButton;
// Set the temporary offset for the favicon. This is used during the crash
// animation.
@@ -186,7 +194,7 @@ class BaseTab : public AnimationDelegate,
scoped_refptr<AnimationContainer> animation_container_;
- views::ImageButton* close_button_;
+ TabCloseButton* close_button_;
// The current index of the loading animation.
int loading_animation_frame_;
diff --git a/chrome/browser/views/tabs/side_tab.cc b/chrome/browser/views/tabs/side_tab.cc
index bad2b4e..c2448d2 100644
--- a/chrome/browser/views/tabs/side_tab.cc
+++ b/chrome/browser/views/tabs/side_tab.cc
@@ -13,7 +13,6 @@
#include "gfx/path.h"
#include "gfx/skia_util.h"
#include "grit/app_resources.h"
-#include "grit/theme_resources.h"
#include "views/controls/button/image_button.h"
namespace {
@@ -38,11 +37,8 @@ const int kPhantomTabIconAlpha = 100;
// SideTab, public:
SideTab::SideTab(TabController* controller)
- : BaseTab(controller) {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- close_button()->SetBackground(kTextColor,
- rb.GetBitmapNamed(IDR_TAB_CLOSE),
- rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK));
+ : BaseTab(controller, false) {
+ SetCloseButtonColor(kTextColor);
}
SideTab::~SideTab() {
diff --git a/chrome/browser/views/tabs/tab.cc b/chrome/browser/views/tabs/tab.cc
index ec34e42..db4fe0c 100644
--- a/chrome/browser/views/tabs/tab.cc
+++ b/chrome/browser/views/tabs/tab.cc
@@ -23,6 +23,7 @@
#include "grit/theme_resources.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
#include "views/controls/button/image_button.h"
+#include "views/widget/root_view.h"
#include "views/widget/tooltip_manager.h"
#include "views/widget/widget.h"
#include "views/window/non_client_view.h"
@@ -105,7 +106,7 @@ const char Tab::kViewClassName[] = "browser/tabs/Tab";
// Tab, public:
Tab::Tab(TabController* controller)
- : BaseTab(controller),
+ : BaseTab(controller, true),
showing_icon_(false),
showing_close_button_(false),
close_button_color_(NULL),
@@ -232,13 +233,7 @@ void Tab::Paint(gfx::Canvas* canvas) {
PaintIcon(canvas);
// If the close button color has changed, generate a new one.
- if (!close_button_color_ || title_color != close_button_color_) {
- close_button_color_ = title_color;
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- close_button()->SetBackground(close_button_color_,
- rb.GetBitmapNamed(IDR_TAB_CLOSE),
- rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK));
- }
+ SetCloseButtonColor(title_color);
}
void Tab::Layout() {
@@ -286,6 +281,7 @@ void Tab::Layout() {
// Size the Close button.
showing_close_button_ = ShouldShowCloseBox();
+ gfx::Insets near_insets;
if (showing_close_button_) {
int close_button_top =
kTopPadding + kCloseButtonVertFuzz +
@@ -295,10 +291,24 @@ void Tab::Layout() {
close_button_top, close_button_width(),
close_button_height());
close_button()->SetVisible(true);
+ int avail_width = width() - close_button()->bounds().right();
+ if (avail_width > 0) {
+ View* root = GetRootView();
+ if (root) { // Root is null when dragging.
+ // Enable mouse near events for the region from the top of the browser
+ // to the bottom of the tab.
+ gfx::Point loc;
+ ConvertPointToView(close_button(), GetRootView(), &loc);
+ near_insets.Set(loc.y(), close_button()->x(),
+ height() - close_button()->bounds().bottom(),
+ avail_width);
+ }
+ }
} else {
close_button()->SetBounds(0, 0, 0, 0);
close_button()->SetVisible(false);
}
+ close_button()->RegisterForMouseNearEvents(near_insets);
int title_left = favicon_bounds_.right() + kFavIconTitleSpacing;
int title_top = kTopPadding + (content_height - font_height()) / 2;
diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc
index 77fb8eb..a1631fd 100644
--- a/chrome/browser/views/tabs/tab_strip.cc
+++ b/chrome/browser/views/tabs/tab_strip.cc
@@ -292,8 +292,13 @@ void TabStrip::SelectTabAt(int old_model_index, int new_model_index) {
}
if (old_model_index >= 0) {
- GetTabAtTabDataIndex(ModelIndexToTabIndex(old_model_index))->
- StopMiniTabTitleAnimation();
+ Tab* tab = GetTabAtTabDataIndex(ModelIndexToTabIndex(old_model_index));
+ tab->StopMiniTabTitleAnimation();
+ tab->SelectedChanged();
+ }
+ if (new_model_index >= 0) {
+ GetTabAtTabDataIndex(
+ ModelIndexToTabIndex(new_model_index))->SelectedChanged();
}
}
diff --git a/views/controls/button/image_button.h b/views/controls/button/image_button.h
index f65118a..c99c77f 100644
--- a/views/controls/button/image_button.h
+++ b/views/controls/button/image_button.h
@@ -18,6 +18,14 @@ namespace views {
class ImageButton : public CustomButton {
public:
+ enum HorizontalAlignment { ALIGN_LEFT = 0,
+ ALIGN_CENTER,
+ ALIGN_RIGHT, };
+
+ enum VerticalAlignment { ALIGN_TOP = 0,
+ ALIGN_MIDDLE,
+ ALIGN_BOTTOM };
+
explicit ImageButton(ButtonListener* listener);
virtual ~ImageButton();
@@ -29,13 +37,10 @@ class ImageButton : public CustomButton {
const SkBitmap* image,
const SkBitmap* mask);
- enum HorizontalAlignment { ALIGN_LEFT = 0,
- ALIGN_CENTER,
- ALIGN_RIGHT, };
-
- enum VerticalAlignment { ALIGN_TOP = 0,
- ALIGN_MIDDLE,
- ALIGN_BOTTOM };
+ // Explicitly sets the background image.
+ void set_background_image(const SkBitmap& background) {
+ background_image_ = background;
+ }
// Sets how the image is laid out within the button's bounds.
void SetImageAlignment(HorizontalAlignment h_align,
diff --git a/views/event.h b/views/event.h
index 6f96a71..91c0a53 100644
--- a/views/event.h
+++ b/views/event.h
@@ -41,6 +41,8 @@ class Event {
ET_MOUSE_MOVED,
ET_MOUSE_ENTERED,
ET_MOUSE_EXITED,
+ ET_MOUSE_NEAR,
+ ET_MOUSE_EXITED_NEAR,
ET_KEY_PRESSED,
ET_KEY_RELEASED,
ET_MOUSEWHEEL,
@@ -100,7 +102,9 @@ class Event {
type_ == ET_MOUSE_MOVED ||
type_ == ET_MOUSE_ENTERED ||
type_ == ET_MOUSE_EXITED ||
- type_ == ET_MOUSEWHEEL;
+ type_ == ET_MOUSEWHEEL ||
+ type_ == ET_MOUSE_NEAR ||
+ type_ == ET_MOUSE_EXITED_NEAR;
}
#if defined(OS_WIN)
diff --git a/views/view.cc b/views/view.cc
index 012240a..988e76b 100644
--- a/views/view.cc
+++ b/views/view.cc
@@ -132,6 +132,21 @@ void View::SetBounds(const gfx::Rect& bounds) {
}
}
+void View::RegisterForMouseNearEvents(const gfx::Insets& insets) {
+ RootView* root = GetRootView();
+ if (insets.empty()) {
+ near_insets_.reset(NULL);
+ if (root)
+ root->UnregisterViewForNearNotification(this);
+ } else {
+ near_insets_.reset(
+ new gfx::Insets(insets.top(), insets.left(), insets.bottom(),
+ insets.right()));
+ if (root)
+ root->RegisterViewForNearNotification(this);
+ }
+}
+
gfx::Rect View::GetLocalBounds(bool include_border) const {
if (include_border || !border_.get())
return gfx::Rect(0, 0, width(), height());
@@ -537,7 +552,7 @@ void View::AddChildView(int index, View* v) {
UpdateTooltip();
RootView* root = GetRootView();
if (root)
- RegisterChildrenForVisibleBoundsNotification(root, v);
+ RegisterChildrenForRootNotifications(root, v);
if (layout_manager_.get())
layout_manager_->ViewAdded(this, v);
@@ -609,7 +624,7 @@ void View::DoRemoveChildView(View* a_view,
RootView* root = GetRootView();
if (root)
- UnregisterChildrenForVisibleBoundsNotification(root, a_view);
+ UnregisterChildrenForRootNotifications(root, a_view);
a_view->PropagateRemoveNotifications(this);
a_view->SetParent(NULL);
@@ -1440,24 +1455,26 @@ ThemeProvider* View::GetThemeProvider() const {
}
// static
-void View::RegisterChildrenForVisibleBoundsNotification(
- RootView* root, View* view) {
+void View::RegisterChildrenForRootNotifications(RootView* root, View* view) {
DCHECK(root && view);
if (view->GetNotifyWhenVisibleBoundsInRootChanges())
root->RegisterViewForVisibleBoundsNotification(view);
+ if (view->near_insets_.get())
+ root->RegisterViewForNearNotification(view);
for (int i = 0; i < view->GetChildViewCount(); ++i)
- RegisterChildrenForVisibleBoundsNotification(root, view->GetChildViewAt(i));
+ RegisterChildrenForRootNotifications(root, view->GetChildViewAt(i));
}
// static
-void View::UnregisterChildrenForVisibleBoundsNotification(
+void View::UnregisterChildrenForRootNotifications(
RootView* root, View* view) {
DCHECK(root && view);
if (view->GetNotifyWhenVisibleBoundsInRootChanges())
root->UnregisterViewForVisibleBoundsNotification(view);
+ if (view->near_insets_.get())
+ root->UnregisterViewForNearNotification(view);
for (int i = 0; i < view->GetChildViewCount(); ++i)
- UnregisterChildrenForVisibleBoundsNotification(root,
- view->GetChildViewAt(i));
+ UnregisterChildrenForRootNotifications(root, view->GetChildViewAt(i));
}
void View::AddDescendantToNotify(View* view) {
diff --git a/views/view.h b/views/view.h
index c0f6f07..a9012a5 100644
--- a/views/view.h
+++ b/views/view.h
@@ -178,6 +178,11 @@ class View : public AcceleratorTarget {
void SetX(int x) { SetBounds(x, y(), width(), height()); }
void SetY(int y) { SetBounds(x(), y, width(), height()); }
+ // Registers this view for mouse near events (OnMouseNear and
+ // OnMouseExitedNear). Mouse near events are sent for the extended rectangle
+ // defined by the bounds of this view + |insets|.
+ void RegisterForMouseNearEvents(const gfx::Insets& insets);
+
// Returns the left coordinate of the View, relative to the parent View,
// which is the value of bounds_.x().
//
@@ -686,6 +691,18 @@ class View : public AcceleratorTarget {
// Default implementation does nothing. Override as needed.
virtual void OnMouseExited(const MouseEvent& event);
+ // Sent when the mouse enters the rectangle defined by this views bounds and
+ // the insets passed to RegisterForMouseNearEvents. This is only sent for
+ // views that have explicitly registered for near notification
+ // (RegisterForMouseNearEvents).
+ virtual void OnMouseNear(const MouseEvent& event) {}
+
+ // Sent when the mouse exits the rectangle defined by this views bounds and
+ // the insets passed to RegisterForMouseNearEvents. This is only sent for
+ // views that have explicitly registered for near notification
+ // (RegisterForMouseNearEvents).
+ virtual void OnMouseExitedNear(const MouseEvent& event) {}
+
// Set the MouseHandler for a drag session.
//
// A drag session is a stream of mouse events starting
@@ -1176,11 +1193,11 @@ class View : public AcceleratorTarget {
// Recursively descends through all descendant views,
// registering/unregistering all views that want visible bounds in root
- // view notification.
- static void RegisterChildrenForVisibleBoundsNotification(RootView* root,
- View* view);
- static void UnregisterChildrenForVisibleBoundsNotification(RootView* root,
- View* view);
+ // view notification and/or mouse near events.
+ static void RegisterChildrenForRootNotifications(RootView* root, View* view);
+ static void UnregisterChildrenForRootNotifications(RootView* root,
+ View* view);
+
// Adds/removes view to the list of descendants that are notified any time
// this views location and possibly size are changed.
@@ -1280,6 +1297,9 @@ class View : public AcceleratorTarget {
// right-to-left locales for this View.
bool flip_canvas_on_paint_for_rtl_ui_;
+ // Insets passed to RegisterForMouseNearEvents.
+ scoped_ptr<gfx::Insets> near_insets_;
+
// The default value for how long to wait (in ms) before showing a menu
// button on hover. This value is used if the OS doesn't supply one.
static const int kShowFolderDropMenuDelay;
diff --git a/views/widget/root_view.cc b/views/widget/root_view.cc
index 3ea101c..2a6798c 100644
--- a/views/widget/root_view.cc
+++ b/views/widget/root_view.cc
@@ -489,6 +489,33 @@ void RootView::OnMouseMoved(const MouseEvent& e) {
mouse_move_handler_->OnMouseExited(exited_event);
SetActiveCursor(NULL);
}
+
+ if (registered_near_views_.empty())
+ return;
+
+ std::set<View*> near_views;
+ GetViewsRegisteredForNearNotification(e, &near_views);
+
+ MouseEvent exited_near_event(Event::ET_MOUSE_EXITED_NEAR, 0, 0, 0);
+ for (std::set<View*>::const_iterator i = near_views_.begin();
+ i != near_views_.end(); ++i) {
+ if (near_views.find(*i) == near_views.end())
+ (*i)->OnMouseExitedNear(exited_near_event);
+ }
+
+ for (std::set<View*>::const_iterator i = near_views.begin();
+ i != near_views.end(); ++i) {
+ if (near_views_.find(*i) == near_views_.end()) {
+ MouseEvent entered_event(Event::ET_MOUSE_ENTERED,
+ this,
+ *i,
+ e.location(),
+ 0);
+ (*i)->OnMouseNear(entered_event);
+ }
+ }
+
+ near_views_.swap(near_views);
}
void RootView::ProcessOnMouseExited() {
@@ -497,6 +524,8 @@ void RootView::ProcessOnMouseExited() {
mouse_move_handler_->OnMouseExited(exited_event);
mouse_move_handler_ = NULL;
}
+
+ SendMouseExitedNear();
}
void RootView::SetMouseHandler(View *new_mh) {
@@ -880,6 +909,14 @@ void RootView::UnregisterViewForVisibleBoundsNotification(View* view) {
}
}
+void RootView::RegisterViewForNearNotification(View* view) {
+ registered_near_views_.insert(view);
+}
+
+void RootView::UnregisterViewForNearNotification(View* view) {
+ registered_near_views_.erase(view);
+}
+
void RootView::SetMouseLocationAndFlags(const MouseEvent& e) {
last_mouse_event_flags_ = e.GetFlags();
last_mouse_event_x_ = e.x();
@@ -936,4 +973,38 @@ void RootView::SetActiveCursor(gfx::NativeCursor cursor) {
#endif
}
+void RootView::GetViewsRegisteredForNearNotification(
+ const MouseEvent& e,
+ std::set<View*>* near_views) {
+ const gfx::Point& location = e.location();
+ for (std::set<View*>::const_iterator i = registered_near_views_.begin();
+ i != registered_near_views_.end(); ++i) {
+ View* view = *i;
+ DCHECK(view->near_insets_.get());
+ const gfx::Insets& insets = *view->near_insets_;
+ gfx::Point view_loc(view->x() - insets.left(),
+ view->y() - insets.top());
+ View::ConvertPointToView(view->GetParent(), this, &view_loc);
+ if (location.x() >= view_loc.x() &&
+ location.y() >= view_loc.y() &&
+ location.x() < view_loc.x() + (view->width() + insets.width()) &&
+ location.y() < view_loc.y() + (view->height() + insets.height())) {
+ near_views->insert(view);
+ }
+ }
+}
+
+void RootView::SendMouseExitedNear() {
+ if (near_views_.empty())
+ return;
+
+ MouseEvent exited_near_event(Event::ET_MOUSE_EXITED_NEAR, 0, 0, 0);
+ for (std::set<View*>::const_iterator i = near_views_.begin();
+ i != near_views_.end(); ++i) {
+ (*i)->OnMouseExitedNear(exited_near_event);
+ }
+
+ near_views_.clear();
+}
+
} // namespace views
diff --git a/views/widget/root_view.h b/views/widget/root_view.h
index 05887e1..ba1d5d22 100644
--- a/views/widget/root_view.h
+++ b/views/widget/root_view.h
@@ -5,6 +5,7 @@
#ifndef VIEWS_WIDGET_ROOT_VIEW_H_
#define VIEWS_WIDGET_ROOT_VIEW_H_
+#include <set>
#include <string>
#include "base/ref_counted.h"
@@ -217,6 +218,10 @@ class RootView : public View,
void RegisterViewForVisibleBoundsNotification(View* view);
void UnregisterViewForVisibleBoundsNotification(View* view);
+ // Registers/unregisters the View for mouse near events.
+ void RegisterViewForNearNotification(View* view);
+ void UnregisterViewForNearNotification(View* view);
+
// Returns the next focusable view or view containing a FocusTraversable (NULL
// if none was found), starting at the starting_view.
// check_starting_view, can_go_up and can_go_down controls the traversal of
@@ -258,6 +263,15 @@ class RootView : public View,
// Sets the current cursor, or resets it to the last one if NULL is provided.
void SetActiveCursor(gfx::NativeCursor cursor);
+ // Returns in |near_views| the set of views registered for mouse near events
+ // that overlap with the location of the specified event.
+ void GetViewsRegisteredForNearNotification(const MouseEvent& e,
+ std::set<View*>* near_views);
+
+ // Sends OnMouseExitedNear to the set of views the mouse is near and empties
+ // the set.
+ void SendMouseExitedNear();
+
// The view currently handing down - drag - up
View* mouse_pressed_handler_;
@@ -330,6 +344,16 @@ class RootView : public View,
bool is_processing_paint_;
#endif
+ // Set of views registered for mouse near events.
+ // NOTE: because views registered for near mouse events can overlap other
+ // views and extend outside the bounds of themselves and ancestors we store
+ // the registered views here and treat them separately. This is generally ok
+ // as there are a small set of views registered for near notification.
+ std::set<View*> registered_near_views_;
+
+ // Set of views the mouse is currently near.
+ std::set<View*> near_views_;
+
DISALLOW_COPY_AND_ASSIGN(RootView);
};