Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1922)

Unified Diff: chromecast/graphics/cast_focus_client_aura.cc

Issue 2636303002: [Chromecast] Add support for z-order and window focus. (Closed)
Patch Set: Simplifications and unit tests. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chromecast/graphics/cast_focus_client_aura.cc
diff --git a/chromecast/graphics/cast_focus_client_aura.cc b/chromecast/graphics/cast_focus_client_aura.cc
new file mode 100644
index 0000000000000000000000000000000000000000..712aec4d0ded9d29485d06016e417d95988bc0a1
--- /dev/null
+++ b/chromecast/graphics/cast_focus_client_aura.cc
@@ -0,0 +1,233 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/graphics/cast_focus_client_aura.h"
+
+#include "ui/aura/window.h"
+
+#define LOG_WINDOW_INFO(top_level, window) \
+ "top-level: " << (top_level)->id() << ": '" << (top_level)->GetName() \
+ << "', window: " << (window)->id() << ": " \
+ << (window)->GetName()
+
+namespace chromecast {
+
+CastFocusClientAura::CastFocusClientAura() : focused_window_(nullptr) {}
+
+CastFocusClientAura::~CastFocusClientAura() {
+ reset();
+}
+
+void CastFocusClientAura::reset() {
+ focused_window_ = nullptr;
+ for (auto it = focusable_windows_.begin(); it != focusable_windows_.end();
+ ++it) {
halliwell 2017/01/24 00:54:46 minor: can this be a range-based loop?
Joshua LeVasseur 2017/01/24 03:39:50 Done.
+ (*it)->RemoveObserver(this);
+ }
+ focusable_windows_.clear();
+}
+
+aura::Window* CastFocusClientAura::GetFocusedWindow() {
+ return focused_window_;
+}
+
+void CastFocusClientAura::AddObserver(
+ aura::client::FocusChangeObserver* observer) {
+ focus_observers_.AddObserver(observer);
+}
+
+void CastFocusClientAura::RemoveObserver(
+ aura::client::FocusChangeObserver* observer) {
+ focus_observers_.RemoveObserver(observer);
+}
+
+void CastFocusClientAura::OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) {
+ if (!visible && (window == focused_window_)) {
+ // The focused window just lost visibility, so de-focus it.
+ UpdateWindowFocus();
+ } else if (visible) {
+ // The window that just became visible might be the most appropriate window
+ // to have focus.
+ UpdateWindowFocus();
+ }
+}
+
+// One of our observed windows is being destroyed.
+// We observe each window that has the potential for being focused,
+// so this window needs to be removed from the list of focusable windows.
+void CastFocusClientAura::OnWindowDestroying(aura::Window* window) {
+ aura::Window* top_level = GetTopLevelWindow(window);
+ DCHECK(top_level);
+ LOG(INFO) << "Removing window, " << LOG_WINDOW_INFO(top_level, window);
+
+ auto iter =
+ std::find(focusable_windows_.begin(), focusable_windows_.end(), window);
+ if (iter != focusable_windows_.end()) {
+ focusable_windows_.erase(iter);
+ window->RemoveObserver(this);
+ }
+ if (window == focused_window_) {
+ // De-focus the window that is being destroyed.
+ UpdateWindowFocus();
+ }
+}
+
+// Update focus if a window is entering or leaving our hierarchy.
+void CastFocusClientAura::OnWindowHierarchyChanging(
+ const HierarchyChangeParams& params) {
+ if (params.new_parent &&
+ (aura::client::GetFocusClient(params.new_parent) == this)) {
+ if (params.old_parent == params.new_parent) {
+ // A window is moving within our hierarchy.
+ return;
+ } else {
+ // A window is entering our hierarchy, so we need to consider
+ // focusing it.
+ FocusWindow(params.target);
+ return;
+ }
+ }
+
+ // The window is leaving our hierarchy, so stop tracking it.
+ // It could contain multiple windows that were focused, so lets stop tracking
+ // them all.
+ auto iter = focusable_windows_.begin();
+ bool was_focused = false;
+ while (iter != focusable_windows_.end()) {
+ aura::Window* window = *iter;
+ if (params.target == window || params.target->Contains(window)) {
+ window->RemoveObserver(this);
+ was_focused |= window == focused_window_;
+ iter = focusable_windows_.erase(iter);
+
+ aura::Window* top_level = GetTopLevelWindow(window);
+ DCHECK(top_level);
+ LOG(INFO) << "Dropping window, " << LOG_WINDOW_INFO(top_level, window);
+ } else {
+ ++iter;
+ }
+ }
+
+ if (was_focused) {
+ // The window that was removed from our hierarchy was the focused window, so
+ // de-focus it.
+ UpdateWindowFocus();
+ }
+}
+
+// An explicit request to focus a window.
+// We lock focus to the top-most high-level window, and so will ignore this
+// focus request if it isn't for the topmost window. If it is for a lower
+// window, then we'll track it to focus it later when it rises to the top.
+void CastFocusClientAura::FocusWindow(aura::Window* window) {
+ if (window) {
+ if (!window->CanFocus()) {
+ return;
+ }
+ aura::Window* top_level = GetTopLevelWindow(window);
+ DCHECK(top_level);
+ LOG(INFO) << "Requesting focus for " << LOG_WINDOW_INFO(top_level, window);
+ auto iter =
+ std::find(focusable_windows_.begin(), focusable_windows_.end(), window);
+ if (iter == focusable_windows_.end()) {
+ // We're not yet tracking this focusable window, so start tracking it as a
+ // potential focus target.
+ window->AddObserver(this);
+ focusable_windows_.push_back(window);
+ }
+ }
+
+ // Check whether this new window is the most appropriate to focus.
+ UpdateWindowFocus();
+}
+
+// Finds the top-most window, and if it doesn't have focus, then gives it focus.
+void CastFocusClientAura::UpdateWindowFocus() {
+ aura::Window* window = GetWindowToFocus();
+ if (window == focused_window_) {
+ return;
+ }
+
+ if (window) {
+ aura::Window* top_level = GetTopLevelWindow(window);
+ DCHECK(top_level);
+ LOG(INFO) << "Switching focus to " << LOG_WINDOW_INFO(top_level, window);
+ }
+
+ aura::Window* unfocus_window = focused_window_;
+ focused_window_ = window;
+
+ for (aura::client::FocusChangeObserver& observer : focus_observers_) {
+ observer.OnWindowFocused(focused_window_, unfocus_window);
+ if (focused_window_ != window) {
+ // The observer changed focused_window_.
+ return;
+ }
+ }
+
+ if (unfocus_window) {
+ aura::client::FocusChangeObserver* focus_observer =
+ aura::client::GetFocusChangeObserver(unfocus_window);
+ if (focus_observer) {
+ focus_observer->OnWindowFocused(focused_window_, unfocus_window);
+ if (focused_window_ != window) {
+ // The observer changed focused_window_.
+ return;
+ }
+ }
+ }
+ if (focused_window_) {
+ aura::client::FocusChangeObserver* focus_observer =
+ aura::client::GetFocusChangeObserver(focused_window_);
+ if (focus_observer) {
+ focus_observer->OnWindowFocused(focused_window_, unfocus_window);
+ if (focused_window_ != window) {
+ // The observer changed focused_window_.
+ return;
+ }
+ }
+ }
+}
+
+// Returns the most appropriate window to have focus.
+// A focusable window could be anywhere within its window hierarchy, and we
+// choose based on the z-order of the top-level window in its hierarchy.
+aura::Window* CastFocusClientAura::GetWindowToFocus() {
+ aura::Window* next = nullptr;
+ aura::Window* next_top_level = nullptr;
+ for (auto iter = focusable_windows_.begin(); iter != focusable_windows_.end();
+ ++iter) {
halliwell 2017/01/24 00:54:46 range-based?
Joshua LeVasseur 2017/01/24 03:39:50 Done.
+ aura::Window* window = *iter;
+ if (!window->CanFocus() || !window->IsVisible()) {
+ continue;
+ }
+
+ // Compare z-order of top-level windows using the window IDs.
+ aura::Window* top_level = GetTopLevelWindow(window);
+ DCHECK(top_level);
+ if (!next || top_level->id() >= next_top_level->id()) {
+ next = window;
+ next_top_level = top_level;
+ }
+ }
+ return next;
+}
+
+aura::Window* CastFocusClientAura::GetTopLevelWindow(aura::Window* window) {
+ while (window->parent() && !window->parent()->GetHost()) {
+ window = window->parent();
+ }
+ return window;
+}
+
+void CastFocusClientAura::ResetFocusWithinActiveWindow(aura::Window* window) {
+ // Sets focus to |window| if it's within the active window (a child of the
+ // focused window).
+ if (focused_window_ && focused_window_->Contains(window)) {
+ FocusWindow(window);
+ }
+}
+
+} // namespace chromecast

Powered by Google App Engine
This is Rietveld 408576698