summaryrefslogtreecommitdiffstats
path: root/views/focus
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/focus
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/focus')
-rw-r--r--views/focus/focus_manager.cc50
-rw-r--r--views/focus/focus_manager.h70
2 files changed, 117 insertions, 3 deletions
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_