Index: services/window_manager/basic_focus_rules.cc |
diff --git a/services/window_manager/basic_focus_rules.cc b/services/window_manager/basic_focus_rules.cc |
index d3b1e0b2b54a9f12fa0736e9f1639eec69b992d5..b5dd91a4e35af598a0ec3c366e026fed5f4c591f 100644 |
--- a/services/window_manager/basic_focus_rules.cc |
+++ b/services/window_manager/basic_focus_rules.cc |
@@ -15,48 +15,156 @@ BasicFocusRules::BasicFocusRules(mojo::View* window_container) |
BasicFocusRules::~BasicFocusRules() {} |
+bool BasicFocusRules::SupportsChildActivation(mojo::View* view) const { |
+ return true; |
+} |
+ |
bool BasicFocusRules::IsToplevelView(mojo::View* view) const { |
- return view->parent() == window_container_; |
+ if (!IsViewParentedToWindowContainer(view)) |
+ return false; |
+ |
+ // The window must exist within a container that supports activation. |
+ // The window cannot be blocked by a modal transient. |
+ return SupportsChildActivation(view->parent()); |
} |
bool BasicFocusRules::CanActivateView(mojo::View* view) const { |
- // TODO(erg): This needs to check visibility, along with focus, and several |
- // other things (see wm::BaseFocusRules). |
- return view->parent() == window_container_; |
+ if (!view) |
+ return true; |
+ |
+ // Only toplevel windows can be activated |
+ if (!IsToplevelView(view)) |
+ return false; |
+ |
+ // The view must be visible. |
+ if (!view->visible()) |
+ return false; |
+ |
+ // TODO(erg): The aura version of this class asks the aura::Window's |
+ // ActivationDelegate whether the window is activatable. |
+ |
+ // A window must be focusable to be activatable. We don't call |
+ // CanFocusWindow() from here because it will call back to us via |
+ // GetActivatableWindow(). |
+ if (!CanFocusViewImpl(view)) |
+ return false; |
+ |
+ // TODO(erg): In the aura version, we also check whether the window is |
+ // blocked by a modal transient window. |
+ |
+ return true; |
} |
bool BasicFocusRules::CanFocusView(mojo::View* view) const { |
- return true; |
+ // It is possible to focus a NULL window, it is equivalent to clearing focus. |
+ if (!view) |
+ return true; |
+ |
+ // The focused view is always inside the active view, so views that aren't |
+ // activatable can't contain the focused view. |
+ View* activatable = GetActivatableView(view); |
+ if (!activatable || !activatable->Contains(view)) |
+ return false; |
+ return CanFocusViewImpl(view); |
} |
mojo::View* BasicFocusRules::GetToplevelView(mojo::View* view) const { |
- while (view->parent() != window_container_) { |
- view = view->parent(); |
- // Unparented hierarchy, there is no "top level" window. |
- if (!view) |
- return nullptr; |
+ View* parent = view->parent(); |
+ View* child = view; |
+ while (parent) { |
+ if (IsToplevelView(child)) |
+ return child; |
+ |
+ parent = parent->parent(); |
+ child = child->parent(); |
} |
- return view; |
+ return nullptr; |
} |
mojo::View* BasicFocusRules::GetActivatableView(mojo::View* view) const { |
- return GetToplevelView(view); |
+ View* parent = view->parent(); |
+ View* child = view; |
+ while (parent) { |
+ if (CanActivateView(child)) |
+ return child; |
+ |
+ // TODO(erg): In the aura version of this class, we have a whole bunch of |
+ // checks to support modal transient windows, and transient parents. |
+ |
+ parent = parent->parent(); |
+ child = child->parent(); |
+ } |
+ |
+ return nullptr; |
} |
mojo::View* BasicFocusRules::GetFocusableView(mojo::View* view) const { |
+ if (CanFocusView(view)) |
+ return view; |
+ |
+ // |view| may be in a hierarchy that is non-activatable, in which case we |
+ // need to cut over to the activatable hierarchy. |
+ View* activatable = GetActivatableView(view); |
+ if (!activatable) { |
+ // There may not be a related activatable hierarchy to cut over to, in which |
+ // case we try an unrelated one. |
+ View* toplevel = GetToplevelView(view); |
+ if (toplevel) |
+ activatable = GetNextActivatableView(toplevel); |
+ if (!activatable) |
+ return nullptr; |
+ } |
+ |
+ if (!activatable->Contains(view)) { |
+ // If there's already a child window focused in the activatable hierarchy, |
+ // just use that (i.e. don't shift focus), otherwise we need to at least cut |
+ // over to the activatable hierarchy. |
+ View* focused = GetFocusableView(activatable); |
+ return activatable->Contains(focused) ? focused : activatable; |
+ } |
+ |
+ while (view && !CanFocusView(view)) |
+ view = view->parent(); |
return view; |
} |
mojo::View* BasicFocusRules::GetNextActivatableView( |
mojo::View* activatable) const { |
- const mojo::View::Children& children = activatable->parent()->children(); |
- for (mojo::View::Children::const_reverse_iterator it = children.rbegin(); |
- it != children.rend(); ++it) { |
- if (*it != activatable) |
- return *it; |
+ DCHECK(activatable); |
+ |
+ // In the basic scenarios handled by BasicFocusRules, the pool of activatable |
+ // windows is limited to the |ignore|'s siblings. |
+ const View::Children& siblings = activatable->parent()->children(); |
+ DCHECK(!siblings.empty()); |
+ |
+ for (auto rit = siblings.rbegin(); rit != siblings.rend(); ++rit) { |
+ View* cur = *rit; |
+ if (cur == activatable) |
+ continue; |
+ if (CanActivateView(cur)) |
+ return cur; |
} |
return nullptr; |
} |
+// TODO(erg): aura::Window::CanFocus() exists. mojo::View::CanFocus() does |
+// not. This is a hack that does everything that Window::CanFocus() currently |
+// does that doesn't require a delegate or an EventClient. |
+bool BasicFocusRules::CanFocusViewImpl(View* view) const { |
+ // TODO(erg): In unit tests, views will never be drawn, so we can't rely on |
+ // IsDrawn() here. |
+ if (IsViewParentedToWindowContainer(view)) |
+ return view->visible(); |
+ |
+ // TODO(erg): Add the intermediary delegate and event client checks once we |
+ // have those. |
+ |
+ return CanFocusViewImpl(view->parent()); |
+} |
+ |
+bool BasicFocusRules::IsViewParentedToWindowContainer(mojo::View* view) const { |
+ return view->parent() == window_container_; |
+} |
+ |
} // namespace mojo |