cast_focus_client_aura.cc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chromecast/graphics/cast_focus_client_aura.h"
  5. #include "base/containers/contains.h"
  6. #include "base/logging.h"
  7. #include "ui/aura/window.h"
  8. #define LOG_WINDOW_INFO(top_level, window) \
  9. "top-level: " << (top_level)->GetId() << ": '" << (top_level)->GetName() \
  10. << "', window: " << (window)->GetId() << ": '" \
  11. << (window)->GetName() << "'"
  12. namespace chromecast {
  13. CastFocusClientAura::CastFocusClientAura() : focused_window_(nullptr) {}
  14. CastFocusClientAura::~CastFocusClientAura() {
  15. focused_window_ = nullptr;
  16. for (aura::Window* window : focusable_windows_) {
  17. window->RemoveObserver(this);
  18. }
  19. focusable_windows_.clear();
  20. }
  21. aura::Window* CastFocusClientAura::GetFocusedWindow() {
  22. return focused_window_;
  23. }
  24. void CastFocusClientAura::AddObserver(
  25. aura::client::FocusChangeObserver* observer) {
  26. focus_observers_.AddObserver(observer);
  27. }
  28. void CastFocusClientAura::RemoveObserver(
  29. aura::client::FocusChangeObserver* observer) {
  30. focus_observers_.RemoveObserver(observer);
  31. }
  32. void CastFocusClientAura::OnWindowVisibilityChanged(aura::Window* window,
  33. bool visible) {
  34. if (!visible && (window == focused_window_)) {
  35. // The focused window just lost visibility, so de-focus it.
  36. UpdateWindowFocus();
  37. } else if (visible) {
  38. // The window that just became visible might be the most appropriate window
  39. // to have focus.
  40. UpdateWindowFocus();
  41. }
  42. }
  43. // One of our observed windows is being destroyed.
  44. // We observe each window that has the potential for being focused,
  45. // so this window needs to be removed from the list of focusable windows.
  46. void CastFocusClientAura::OnWindowDestroying(aura::Window* window) {
  47. aura::Window* top_level = GetZOrderWindow(window);
  48. DCHECK(top_level);
  49. DLOG(INFO) << "Removing window, " << LOG_WINDOW_INFO(top_level, window);
  50. auto iter =
  51. std::find(focusable_windows_.begin(), focusable_windows_.end(), window);
  52. if (iter != focusable_windows_.end()) {
  53. focusable_windows_.erase(iter);
  54. window->RemoveObserver(this);
  55. }
  56. if (window == focused_window_) {
  57. // De-focus the window that is being destroyed.
  58. UpdateWindowFocus();
  59. }
  60. }
  61. // Update focus if a window is entering or leaving our hierarchy.
  62. void CastFocusClientAura::OnWindowHierarchyChanging(
  63. const HierarchyChangeParams& params) {
  64. if (params.new_parent &&
  65. (aura::client::GetFocusClient(params.new_parent) == this)) {
  66. if (params.old_parent == params.new_parent) {
  67. // A window is moving within our hierarchy.
  68. return;
  69. } else {
  70. // A window is entering our hierarchy, so we need to consider
  71. // focusing it.
  72. FocusWindow(params.target);
  73. return;
  74. }
  75. }
  76. // The window is leaving our hierarchy, so stop tracking it.
  77. // It could contain multiple windows that were focused, so lets stop tracking
  78. // them all.
  79. auto iter = focusable_windows_.begin();
  80. bool was_focused = false;
  81. while (iter != focusable_windows_.end()) {
  82. aura::Window* window = *iter;
  83. if (params.target == window || params.target->Contains(window)) {
  84. window->RemoveObserver(this);
  85. was_focused |= window == focused_window_;
  86. iter = focusable_windows_.erase(iter);
  87. aura::Window* top_level = GetZOrderWindow(window);
  88. DCHECK(top_level);
  89. DLOG(INFO) << "Dropping window, " << LOG_WINDOW_INFO(top_level, window);
  90. } else {
  91. ++iter;
  92. }
  93. }
  94. if (was_focused) {
  95. // The window that was removed from our hierarchy was the focused window, so
  96. // de-focus it.
  97. UpdateWindowFocus();
  98. }
  99. }
  100. // An explicit request to focus a window.
  101. // We lock focus to the top-most high-level window, and so will ignore this
  102. // focus request if it isn't for the topmost window. If it is for a lower
  103. // window, then we'll track it to focus it later when it rises to the top.
  104. void CastFocusClientAura::FocusWindow(aura::Window* window) {
  105. if (window) {
  106. if (!window->CanFocus()) {
  107. return;
  108. }
  109. aura::Window* top_level = GetZOrderWindow(window);
  110. DCHECK(top_level);
  111. DLOG(INFO) << "Requesting focus for " << LOG_WINDOW_INFO(top_level, window);
  112. if (!base::Contains(focusable_windows_, window)) {
  113. // We're not yet tracking this focusable window, so start tracking it as a
  114. // potential focus target.
  115. window->AddObserver(this);
  116. focusable_windows_.push_back(window);
  117. }
  118. }
  119. // Check whether this new window is the most appropriate to focus.
  120. UpdateWindowFocus();
  121. }
  122. // Finds the top-most window, and if it doesn't have focus, then gives it focus.
  123. void CastFocusClientAura::UpdateWindowFocus() {
  124. aura::Window* window = GetWindowToFocus();
  125. if (window == focused_window_) {
  126. return;
  127. }
  128. if (window) {
  129. aura::Window* top_level = GetZOrderWindow(window);
  130. DCHECK(top_level);
  131. DLOG(INFO) << "Switching focus to " << LOG_WINDOW_INFO(top_level, window);
  132. }
  133. aura::Window* unfocus_window = focused_window_;
  134. focused_window_ = window;
  135. for (aura::client::FocusChangeObserver& observer : focus_observers_) {
  136. observer.OnWindowFocused(focused_window_, unfocus_window);
  137. if (focused_window_ != window) {
  138. // The observer changed focused_window_.
  139. return;
  140. }
  141. }
  142. if (unfocus_window) {
  143. aura::client::FocusChangeObserver* focus_observer =
  144. aura::client::GetFocusChangeObserver(unfocus_window);
  145. if (focus_observer) {
  146. focus_observer->OnWindowFocused(focused_window_, unfocus_window);
  147. if (focused_window_ != window) {
  148. // The observer changed focused_window_.
  149. return;
  150. }
  151. }
  152. }
  153. if (focused_window_) {
  154. aura::client::FocusChangeObserver* focus_observer =
  155. aura::client::GetFocusChangeObserver(focused_window_);
  156. if (focus_observer) {
  157. focus_observer->OnWindowFocused(focused_window_, unfocus_window);
  158. if (focused_window_ != window) {
  159. // The observer changed focused_window_.
  160. return;
  161. }
  162. }
  163. }
  164. }
  165. // Returns the most appropriate window to have focus.
  166. // A focusable window could be anywhere within its window hierarchy, and we
  167. // choose based on the z-order of the top-level window in its hierarchy.
  168. aura::Window* CastFocusClientAura::GetWindowToFocus() {
  169. aura::Window* next = nullptr;
  170. aura::Window* next_top_level = nullptr;
  171. for (aura::Window* window : focusable_windows_) {
  172. if (!window->CanFocus() || !window->IsVisible()) {
  173. continue;
  174. }
  175. // Compare z-order of top-level windows using the window IDs.
  176. aura::Window* top_level = GetZOrderWindow(window);
  177. DCHECK(top_level);
  178. if (!next || top_level->GetId() >= next_top_level->GetId()) {
  179. next = window;
  180. next_top_level = top_level;
  181. }
  182. }
  183. return next;
  184. }
  185. const aura::Window* CastFocusClientAura::GetZOrderWindow(
  186. const aura::Window* window) const {
  187. while (window->parent() && !window->parent()->IsRootWindow()) {
  188. window = window->parent();
  189. }
  190. return window;
  191. }
  192. void CastFocusClientAura::ResetFocusWithinActiveWindow(aura::Window* window) {
  193. // Sets focus to |window| if it's within the active window (a child of the
  194. // focused window).
  195. if (focused_window_ && focused_window_->Contains(window)) {
  196. FocusWindow(window);
  197. }
  198. }
  199. void CastFocusClientAura::AddObserver(wm::ActivationChangeObserver* observer) {}
  200. void CastFocusClientAura::RemoveObserver(
  201. wm::ActivationChangeObserver* observer) {}
  202. void CastFocusClientAura::ActivateWindow(aura::Window* window) {}
  203. void CastFocusClientAura::DeactivateWindow(aura::Window* window) {}
  204. const aura::Window* CastFocusClientAura::GetActiveWindow() const {
  205. return nullptr;
  206. }
  207. aura::Window* CastFocusClientAura::GetActivatableWindow(
  208. aura::Window* window) const {
  209. return window;
  210. }
  211. const aura::Window* CastFocusClientAura::GetToplevelWindow(
  212. const aura::Window* window) const {
  213. return GetZOrderWindow(window);
  214. }
  215. bool CastFocusClientAura::CanActivateWindow(const aura::Window* window) const {
  216. return true;
  217. }
  218. } // namespace chromecast