summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authortwiz@chromium.org <twiz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-10 22:46:47 +0000
committertwiz@chromium.org <twiz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-10 22:46:47 +0000
commit2db27be7dbb16f02587b90f150a843d8ad234563 (patch)
tree9bfd5b6b2ff4637a69744ce35249361d1f3ea353 /views
parente1ce144bd04336891bb12b967095e938ed5f1bbb (diff)
downloadchromium_src-2db27be7dbb16f02587b90f150a843d8ad234563.zip
chromium_src-2db27be7dbb16f02587b90f150a843d8ad234563.tar.gz
chromium_src-2db27be7dbb16f02587b90f150a843d8ad234563.tar.bz2
CL implementing focus-dismissal of the chrome.experimental.popup set of extension APIs.
Specifically, these changes cause a displayed pop-up to be dismissed when the focus shifts away from both the pop-up view, and the extension-view that launched the pop-up.I had to do a lot of investigating and trial-and-error to converge to the solution present here. I was hoping to be able to piggy-back on the existing FocusManager's various listener routines, but because the pop-up is hosted in a BubbleWidget, which is a separate top-level window with its own focus manager, I could not rely on a given focus manager to take care of the focus change notifications. To get around the above issue, I added a new type of focus listener that can listen on native-window focus change events. I added invocations to this listener throughout the Widget classes in the system so that registered listeners will be notified on focus change. I found some of the focus change events problematic, as the system will arbitrarily reassign the focus to the main browser window when shifting activation between chrome windows. (SeefocusManagerWin::ClearNativeFocus). To prevent this focus bounce from confusing focus listeners, I added a means to suppress notification of focus change during these operations. I added GTK and Mac stubs for the new widget functions that will assert when called. GTK and Cocoa development is not my expertise, so I thought // TODO(port) would be wiser.I'm uncertain of the best means to unit-test these changes. Direction in this regard would be appreciated. BUG=None TEST=None Review URL: http://codereview.chromium.org/552167 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38685 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r--views/controls/native/native_view_host.cc15
-rw-r--r--views/controls/native/native_view_host.h1
-rw-r--r--views/focus/focus_manager.cc50
-rw-r--r--views/focus/focus_manager.h70
-rw-r--r--views/view.cc8
-rw-r--r--views/view.h4
-rw-r--r--views/widget/widget.h4
-rw-r--r--views/widget/widget_gtk.cc9
-rw-r--r--views/widget/widget_gtk.h2
-rw-r--r--views/widget/widget_win.cc33
-rw-r--r--views/widget/widget_win.h3
11 files changed, 194 insertions, 5 deletions
diff --git a/views/controls/native/native_view_host.cc b/views/controls/native/native_view_host.cc
index 1274e5c..79abc1d 100644
--- a/views/controls/native/native_view_host.cc
+++ b/views/controls/native/native_view_host.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "app/gfx/canvas.h"
#include "views/controls/native/native_view_host_wrapper.h"
+#include "views/widget/root_view.h"
#include "views/widget/widget.h"
namespace views {
@@ -145,6 +146,20 @@ void NativeViewHost::Focus() {
native_wrapper_->SetFocus();
}
+bool NativeViewHost::ContainsNativeView(gfx::NativeView native_view) const {
+ if (native_view == native_view_)
+ return true;
+
+ views::Widget* native_widget =
+ views::Widget::GetWidgetFromNativeView(native_view_);
+ views::RootView* root_view =
+ native_widget ? native_widget->GetRootView() : NULL;
+ if (root_view && root_view->ContainsNativeView(native_view))
+ return true;
+
+ return View::ContainsNativeView(native_view);
+}
+
////////////////////////////////////////////////////////////////////////////////
// NativeViewHost, private:
diff --git a/views/controls/native/native_view_host.h b/views/controls/native/native_view_host.h
index d6115b2..5a9a403 100644
--- a/views/controls/native/native_view_host.h
+++ b/views/controls/native/native_view_host.h
@@ -74,6 +74,7 @@ class NativeViewHost : public View {
virtual void Paint(gfx::Canvas* canvas);
virtual void VisibilityChanged(View* starting_from, bool is_visible);
virtual void Focus();
+ virtual bool ContainsNativeView(gfx::NativeView native_view) const;
protected:
virtual void VisibleBoundsInRootChanged();
diff --git a/views/focus/focus_manager.cc b/views/focus/focus_manager.cc
index 9942854..f637e09 100644
--- a/views/focus/focus_manager.cc
+++ b/views/focus/focus_manager.cc
@@ -22,6 +22,46 @@
namespace views {
+// FocusManager::WidgetFocusManager ---------------------------------
+
+void FocusManager::WidgetFocusManager::AddFocusChangeListener(
+ WidgetFocusChangeListener* listener) {
+ DCHECK(std::find(focus_change_listeners_.begin(),
+ focus_change_listeners_.end(), listener) ==
+ focus_change_listeners_.end()) <<
+ "Adding a WidgetFocusChangeListener twice.";
+ focus_change_listeners_.push_back(listener);
+}
+
+void FocusManager::WidgetFocusManager::RemoveFocusChangeListener(
+ WidgetFocusChangeListener* listener) {
+ WidgetFocusChangeListenerList::iterator iter(std::find(
+ focus_change_listeners_.begin(),
+ focus_change_listeners_.end(),
+ listener));
+ if (iter != focus_change_listeners_.end()) {
+ focus_change_listeners_.erase(iter);
+ } else {
+ NOTREACHED() <<
+ "Attempting to remove an unregistered WidgetFocusChangeListener.";
+ }
+}
+
+void FocusManager::WidgetFocusManager::OnWidgetFocusEvent(
+ gfx::NativeView focused_before,
+ gfx::NativeView focused_now) {
+ if (!enabled_)
+ return;
+
+ // Perform a safe iteration over the focus listeners, as the array of
+ // may change during notification.
+ WidgetFocusChangeListenerList local_listeners(focus_change_listeners_);
+ WidgetFocusChangeListenerList::iterator iter(local_listeners.begin());
+ for (;iter != local_listeners.end(); ++iter) {
+ (*iter)->NativeFocusWillChange(focused_before, focused_now);
+ }
+}
+
// FocusManager -----------------------------------------------------
FocusManager::FocusManager(Widget* widget)
@@ -277,7 +317,15 @@ void FocusManager::StoreFocusedView() {
view_storage->StoreView(stored_focused_view_storage_id_, focused_view_);
View* v = focused_view_;
- ClearFocus();
+
+ {
+ // Temporarily disable notification. ClearFocus() will set the focus to the
+ // main browser window. This extra focus bounce which happens during
+ // deactivation can confuse registered WidgetFocusListeners, as the focus
+ // is not changing due to a user-initiated event.
+ AutoNativeNotificationDisabler local_notification_disabler;
+ ClearFocus();
+ }
if (v)
v->SchedulePaint(); // Remove focus border.
diff --git a/views/focus/focus_manager.h b/views/focus/focus_manager.h
index aec8911..00e7a0a 100644
--- a/views/focus/focus_manager.h
+++ b/views/focus/focus_manager.h
@@ -5,12 +5,13 @@
#ifndef VIEWS_FOCUS_FOCUS_MANAGER_H_
#define VIEWS_FOCUS_FOCUS_MANAGER_H_
-#include <vector>
-#include <map>
#include <list>
+#include <map>
+#include <vector>
#include "app/gfx/native_widget_types.h"
#include "base/basictypes.h"
+#include "base/singleton.h"
#include "views/accelerator.h"
// The FocusManager class is used to handle focus traversal, store/restore
@@ -132,11 +133,61 @@ class FocusChangeListener {
virtual void FocusWillChange(View* focused_before, View* focused_now) = 0;
};
+// This interface should be implemented by classes that want to be notified when
+// the native focus is about to change. Listeners implementing this interface
+// will be invoked for all native focus changes across the entire Chrome
+// application. FocusChangeListeners are only called for changes within the
+// children of a single top-level native-view.
+class WidgetFocusChangeListener {
+ public:
+ virtual void NativeFocusWillChange(gfx::NativeView focused_before,
+ gfx::NativeView focused_now) = 0;
+};
+
class FocusManager {
public:
+ class WidgetFocusManager {
+ public:
+ // Adds/removes a WidgetFocusChangeListener |listener| to the set of
+ // active listeners.
+ void AddFocusChangeListener(WidgetFocusChangeListener* listener);
+ void RemoveFocusChangeListener(WidgetFocusChangeListener* listener);
+
+ // To be called when native-focus shifts from |focused_before| to
+ // |focused_now|.
+ // TODO(port) : Invocations to this routine are only implemented for
+ // the Win32 platform. Calls need to be placed appropriately for
+ // non-Windows environments.
+ void OnWidgetFocusEvent(gfx::NativeView focused_before,
+ gfx::NativeView focused_now);
+
+ // Enable/Disable notification of registered listeners during calls
+ // to OnWidgetFocusEvent. Used to prevent unwanted focus changes from
+ // propagating notifications.
+ void EnableNotifications() { enabled_ = true; }
+ void DisableNotifications() { enabled_ = false; }
+
+ private:
+ WidgetFocusManager() : enabled_(true) {}
+
+ typedef std::vector<WidgetFocusChangeListener*>
+ WidgetFocusChangeListenerList;
+ WidgetFocusChangeListenerList focus_change_listeners_;
+
+ bool enabled_;
+
+ friend struct DefaultSingletonTraits<WidgetFocusManager>;
+ DISALLOW_COPY_AND_ASSIGN(WidgetFocusManager);
+ };
+
explicit FocusManager(Widget* widget);
~FocusManager();
+ // Returns the global WidgetFocusManager instance for the running application.
+ static WidgetFocusManager* GetWidgetFocusManager() {
+ return Singleton<WidgetFocusManager>::get();
+ }
+
// Processes the passed key event for accelerators and tab traversal.
// Returns false if the event has been consumed and should not be processed
// further.
@@ -261,6 +312,21 @@ class FocusManager {
DISALLOW_COPY_AND_ASSIGN(FocusManager);
};
+// A basic helper class that is used to disable native focus change
+// notifications within a scope.
+class AutoNativeNotificationDisabler {
+ public:
+ AutoNativeNotificationDisabler() {
+ FocusManager::GetWidgetFocusManager()->DisableNotifications();
+ }
+
+ ~AutoNativeNotificationDisabler() {
+ FocusManager::GetWidgetFocusManager()->EnableNotifications();
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoNativeNotificationDisabler);
+};
+
} // namespace views
#endif // VIEWS_FOCUS_FOCUS_MANAGER_H_
diff --git a/views/view.cc b/views/view.cc
index 53f1102..3b823a8 100644
--- a/views/view.cc
+++ b/views/view.cc
@@ -788,6 +788,14 @@ Window* View::GetWindow() const {
return widget ? widget->GetWindow() : NULL;
}
+bool View::ContainsNativeView(gfx::NativeView native_view) const {
+ for (int i = 0, count = GetChildViewCount(); i < count; ++i) {
+ if (GetChildViewAt(i)->ContainsNativeView(native_view))
+ return true;
+ }
+ return false;
+}
+
// Get the containing RootView
RootView* View::GetRootView() {
Widget* widget = GetWidget();
diff --git a/views/view.h b/views/view.h
index 5ae8be5..b44c04d 100644
--- a/views/view.h
+++ b/views/view.h
@@ -455,6 +455,10 @@ class View : public AcceleratorTarget {
// level windows (as is done for popups, bubbles and menus).
virtual Window* GetWindow() const;
+ // Returns true if the native view |native_view| is contained in the view
+ // hierarchy beneath this view.
+ virtual bool ContainsNativeView(gfx::NativeView native_view) const;
+
// Get the containing RootView
virtual RootView* GetRootView();
diff --git a/views/widget/widget.h b/views/widget/widget.h
index fa8111e..a3959b4 100644
--- a/views/widget/widget.h
+++ b/views/widget/widget.h
@@ -193,6 +193,10 @@ class Widget {
// Forwarded from the RootView so that the widget can do any cleanup.
virtual void ViewHierarchyChanged(bool is_add, View *parent,
View *child) = 0;
+
+ // Returns true if the native view |native_view| is contained in the
+ // views::View hierarchy rooted at this widget.
+ virtual bool ContainsNativeView(gfx::NativeView native_view) = 0;
};
} // namespace views
diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc
index 3f9b3c4..44bc82d 100644
--- a/views/widget/widget_gtk.cc
+++ b/views/widget/widget_gtk.cc
@@ -590,6 +590,12 @@ void WidgetGtk::ViewHierarchyChanged(bool is_add, View *parent,
drop_target_->ResetTargetViewIfEquals(child);
}
+bool WidgetGtk::ContainsNativeView(gfx::NativeView native_view) {
+ // TODO(port) See implementation in WidgetWin::ContainsNativeView.
+ NOTREACHED() << "WidgetGtk::ContainsNativeView is not implemented.";
+ return false;
+}
+
////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, MessageLoopForUI::Observer implementation:
@@ -1239,7 +1245,8 @@ void WidgetGtk::CreateGtkWidget(GtkWidget* parent, const gfx::Rect& bounds) {
gtk_fixed_set_has_window(GTK_FIXED(window_contents_), true);
gtk_container_add(GTK_CONTAINER(widget_), window_contents_);
gtk_widget_show(window_contents_);
- g_object_set_data(G_OBJECT(window_contents_), kWidgetKey, this);
+ g_object_set_data(G_OBJECT(window_contents_), kWidgetKey,
+ static_cast<Widget*>(this));
if (transparent_)
ConfigureWidgetForTransparentBackground();
diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h
index 9d73bf0..0c0096c 100644
--- a/views/widget/widget_gtk.h
+++ b/views/widget/widget_gtk.h
@@ -168,6 +168,8 @@ class WidgetGtk
virtual FocusManager* GetFocusManager();
virtual void ViewHierarchyChanged(bool is_add, View *parent,
View *child);
+ virtual bool ContainsNativeView(gfx::NativeView native_view);
+
// Overridden from MessageLoopForUI::Observer:
virtual void WillProcessEvent(GdkEvent* event);
diff --git a/views/widget/widget_win.cc b/views/widget/widget_win.cc
index 6f75506..ea54036 100644
--- a/views/widget/widget_win.cc
+++ b/views/widget/widget_win.cc
@@ -383,6 +383,28 @@ void WidgetWin::ViewHierarchyChanged(bool is_add, View *parent,
drop_target_->ResetTargetViewIfEquals(child);
}
+
+bool WidgetWin::ContainsNativeView(gfx::NativeView native_view) {
+ if (hwnd() == native_view)
+ return true;
+
+ // Traverse the set of parents of the given view to determine if native_view
+ // is a descendant of this window.
+ HWND parent_window = ::GetParent(native_view);
+ HWND previous_child = native_view;
+ while (parent_window && parent_window != previous_child) {
+ if (hwnd() == parent_window)
+ return true;
+ previous_child = parent_window;
+ parent_window = ::GetParent(parent_window);
+ }
+
+ // A views::NativeViewHost may contain the given native view, without it being
+ // an ancestor of hwnd(), so traverse the views::View hierarchy looking for
+ // such views.
+ return GetRootView()->ContainsNativeView(native_view);
+}
+
////////////////////////////////////////////////////////////////////////////////
// MessageLoop::Observer
@@ -595,6 +617,13 @@ void WidgetWin::OnKeyUp(TCHAR c, UINT rep_cnt, UINT flags) {
SetMsgHandled(root_view->ProcessKeyEvent(event));
}
+void WidgetWin::OnKillFocus(HWND focused_window) {
+ GetFocusManager()->GetWidgetFocusManager()->OnWidgetFocusEvent(
+ this->GetNativeView(),
+ focused_window);
+ SetMsgHandled(FALSE);
+}
+
// TODO(pkasting): ORing the pressed/released button into the flags is _wrong_.
// It makes it impossible to tell which button was modified when multiple
// buttons are/were held down. We need to instead put the modified button into
@@ -800,6 +829,9 @@ LRESULT WidgetWin::OnReflectedMessage(UINT msg,
}
void WidgetWin::OnSetFocus(HWND focused_window) {
+ GetFocusManager()->GetWidgetFocusManager()->OnWidgetFocusEvent(
+ focused_window,
+ this->GetNativeView());
SetMsgHandled(FALSE);
}
@@ -1236,7 +1268,6 @@ void Widget::FindAllRootViews(HWND window,
root_views->push_back(*it);
}
-
////////////////////////////////////////////////////////////////////////////////
// Widget, public:
diff --git a/views/widget/widget_win.h b/views/widget/widget_win.h
index 28a3782..ed233d4 100644
--- a/views/widget/widget_win.h
+++ b/views/widget/widget_win.h
@@ -130,6 +130,7 @@ class WidgetWin : public app::WindowImpl,
MSG_WM_INITMENUPOPUP(OnInitMenuPopup)
MSG_WM_KEYDOWN(OnKeyDown)
MSG_WM_KEYUP(OnKeyUp)
+ MSG_WM_KILLFOCUS(OnKillFocus)
MSG_WM_SYSKEYDOWN(OnKeyDown)
MSG_WM_SYSKEYUP(OnKeyUp)
MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk)
@@ -207,6 +208,7 @@ class WidgetWin : public app::WindowImpl,
virtual FocusManager* GetFocusManager();
virtual void ViewHierarchyChanged(bool is_add, View *parent,
View *child);
+ virtual bool ContainsNativeView(gfx::NativeView native_view);
// Overridden from MessageLoop::Observer:
void WillProcessMessage(const MSG& msg);
@@ -335,6 +337,7 @@ class WidgetWin : public app::WindowImpl,
virtual void OnInitMenuPopup(HMENU menu, UINT position, BOOL is_system_menu);
virtual void OnKeyDown(TCHAR c, UINT rep_cnt, UINT flags);
virtual void OnKeyUp(TCHAR c, UINT rep_cnt, UINT flags);
+ virtual void OnKillFocus(HWND focused_window);
virtual void OnLButtonDblClk(UINT flags, const CPoint& point);
virtual void OnLButtonDown(UINT flags, const CPoint& point);
virtual void OnLButtonUp(UINT flags, const CPoint& point);