window_cycle_list.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. // Copyright 2014 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 "ash/wm/window_cycle/window_cycle_list.h"
  5. #include "ash/accessibility/accessibility_controller_impl.h"
  6. #include "ash/app_list/app_list_controller_impl.h"
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/frame_throttler/frame_throttling_controller.h"
  9. #include "ash/public/cpp/shell_window_ids.h"
  10. #include "ash/public/cpp/window_properties.h"
  11. #include "ash/root_window_controller.h"
  12. #include "ash/shell.h"
  13. #include "ash/shell_delegate.h"
  14. #include "ash/system/unified/unified_system_tray.h"
  15. #include "ash/wm/mru_window_tracker.h"
  16. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  17. #include "ash/wm/window_cycle/window_cycle_controller.h"
  18. #include "ash/wm/window_state.h"
  19. #include "ash/wm/window_util.h"
  20. #include "base/check.h"
  21. #include "base/location.h"
  22. #include "ui/aura/scoped_window_targeter.h"
  23. #include "ui/aura/window.h"
  24. #include "ui/aura/window_targeter.h"
  25. #include "ui/compositor/layer_type.h"
  26. #include "ui/display/display.h"
  27. #include "ui/display/display_observer.h"
  28. #include "ui/display/screen.h"
  29. #include "ui/events/event.h"
  30. #include "ui/views/controls/label.h"
  31. #include "ui/views/widget/widget.h"
  32. #include "ui/wm/core/coordinate_conversion.h"
  33. #include "ui/wm/core/window_animations.h"
  34. namespace ash {
  35. namespace {
  36. bool g_disable_initial_delay = false;
  37. // Delay before the UI fade in animation starts. This is so users can switch
  38. // quickly between windows without bringing up the UI.
  39. constexpr base::TimeDelta kShowDelayDuration = base::Milliseconds(150);
  40. // The alt-tab cycler widget is not activatable (except when ChromeVox is on),
  41. // so we use WindowTargeter to send input events to the widget.
  42. class CustomWindowTargeter : public aura::WindowTargeter {
  43. public:
  44. explicit CustomWindowTargeter(aura::Window* tab_cycler)
  45. : tab_cycler_(tab_cycler) {}
  46. CustomWindowTargeter(const CustomWindowTargeter&) = delete;
  47. CustomWindowTargeter& operator=(const CustomWindowTargeter&) = delete;
  48. ~CustomWindowTargeter() override = default;
  49. // aura::WindowTargeter:
  50. ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
  51. ui::Event* event) override {
  52. if (event->IsLocatedEvent())
  53. return aura::WindowTargeter::FindTargetForEvent(root, event);
  54. return tab_cycler_;
  55. }
  56. private:
  57. aura::Window* tab_cycler_;
  58. };
  59. gfx::Point ConvertEventToScreen(const ui::LocatedEvent* event) {
  60. aura::Window* target = static_cast<aura::Window*>(event->target());
  61. aura::Window* event_root = target->GetRootWindow();
  62. gfx::Point event_screen_point = event->root_location();
  63. wm::ConvertPointToScreen(event_root, &event_screen_point);
  64. return event_screen_point;
  65. }
  66. aura::Window* GetRootWindowForCycleView() {
  67. // Returns the root window for initializing cycle view if tablet mode is
  68. // enabled, or if the feature for alt-tab to follow the cursor is disabled.
  69. if (Shell::Get()->tablet_mode_controller()->InTabletMode() ||
  70. !features::DoWindowsFollowCursor()) {
  71. return Shell::GetRootWindowForNewWindows();
  72. }
  73. // Return the root window the cursor is currently on.
  74. return Shell::GetRootWindowForDisplayId(
  75. Shell::Get()->cursor_manager()->GetDisplay().id());
  76. }
  77. } // namespace
  78. WindowCycleList::WindowCycleList(const WindowList& windows)
  79. : windows_(windows) {
  80. if (!ShouldShowUi())
  81. Shell::Get()->mru_window_tracker()->SetIgnoreActivations(true);
  82. active_window_before_window_cycle_ = window_util::GetActiveWindow();
  83. for (auto* window : windows_)
  84. window->AddObserver(this);
  85. if (ShouldShowUi()) {
  86. // Disable the tab scrubber so three finger scrolling doesn't scrub tabs as
  87. // well.
  88. Shell::Get()->shell_delegate()->SetTabScrubberChromeOSEnabled(false);
  89. if (g_disable_initial_delay) {
  90. InitWindowCycleView();
  91. } else {
  92. show_ui_timer_.Start(FROM_HERE, kShowDelayDuration, this,
  93. &WindowCycleList::InitWindowCycleView);
  94. }
  95. }
  96. }
  97. WindowCycleList::~WindowCycleList() {
  98. if (!ShouldShowUi())
  99. Shell::Get()->mru_window_tracker()->SetIgnoreActivations(false);
  100. Shell::Get()->shell_delegate()->SetTabScrubberChromeOSEnabled(true);
  101. for (auto* window : windows_)
  102. window->RemoveObserver(this);
  103. if (cycle_ui_widget_)
  104. cycle_ui_widget_->Close();
  105. // Store the target window before |cycle_view_| is destroyed.
  106. aura::Window* target_window = nullptr;
  107. // |this| is responsible for notifying |cycle_view_| when windows are
  108. // destroyed. Since |this| is going away, clobber |cycle_view_|. Otherwise
  109. // there will be a race where a window closes after now but before the
  110. // Widget::Close() call above actually destroys |cycle_view_|. See
  111. // crbug.com/681207
  112. if (cycle_view_) {
  113. target_window = GetTargetWindow();
  114. cycle_view_->DestroyContents();
  115. }
  116. // While the cycler widget is shown, the windows listed in the cycler is
  117. // marked as force-visible and don't contribute to occlusion. In order to
  118. // work occlusion calculation properly, we need to activate a window after
  119. // the widget has been destroyed. See b/138914552.
  120. if (!windows_.empty() && user_did_accept_) {
  121. if (!target_window)
  122. target_window = windows_[current_index_];
  123. SelectWindow(target_window);
  124. }
  125. Shell::Get()->frame_throttling_controller()->EndThrottling();
  126. }
  127. aura::Window* WindowCycleList::GetTargetWindow() {
  128. return cycle_view_->target_window();
  129. }
  130. void WindowCycleList::ReplaceWindows(const WindowList& windows) {
  131. RemoveAllWindows();
  132. windows_ = windows;
  133. for (auto* new_window : windows_)
  134. new_window->AddObserver(this);
  135. if (cycle_view_)
  136. cycle_view_->UpdateWindows(windows_);
  137. }
  138. void WindowCycleList::Step(
  139. WindowCycleController::WindowCyclingDirection direction,
  140. bool starting_alt_tab_or_switching_mode) {
  141. if (windows_.empty())
  142. return;
  143. // If the position of the window cycle list is out-of-sync with the currently
  144. // selected item, scroll to the selected item and then step.
  145. if (cycle_view_) {
  146. aura::Window* selected_window = GetTargetWindow();
  147. if (selected_window)
  148. Scroll(GetIndexOfWindow(selected_window) - current_index_);
  149. }
  150. int offset =
  151. direction == WindowCycleController::WindowCyclingDirection::kForward ? 1
  152. : -1;
  153. // When the window highlight should be reset and the first window in the MRU
  154. // cycle list is not the latest active one before entering alt-tab, highlight
  155. // it instead of the second window. This occurs when the user is in overview
  156. // mode, all windows are minimized, or all windows are in other desks.
  157. //
  158. // Note: Simply checking the active status of the first window won't work
  159. // because when the ChromeVox is enabled, the widget is activatable, so the
  160. // first window in MRU becomes inactive.
  161. if (starting_alt_tab_or_switching_mode &&
  162. direction == WindowCycleController::WindowCyclingDirection::kForward &&
  163. active_window_before_window_cycle_ != windows_[0]) {
  164. offset = 0;
  165. current_index_ = 0;
  166. }
  167. SetFocusedWindow(windows_[GetOffsettedWindowIndex(offset)]);
  168. Scroll(offset);
  169. }
  170. void WindowCycleList::Drag(float delta_x) {
  171. DCHECK(cycle_view_);
  172. cycle_view_->Drag(delta_x);
  173. }
  174. void WindowCycleList::StartFling(float velocity_x) {
  175. DCHECK(cycle_view_);
  176. cycle_view_->StartFling(velocity_x);
  177. }
  178. void WindowCycleList::SetFocusedWindow(aura::Window* window) {
  179. if (windows_.empty())
  180. return;
  181. if (ShouldShowUi() && cycle_view_)
  182. cycle_view_->SetTargetWindow(windows_[GetIndexOfWindow(window)]);
  183. }
  184. void WindowCycleList::SetFocusTabSlider(bool focus) {
  185. DCHECK(cycle_view_);
  186. cycle_view_->SetFocusTabSlider(focus);
  187. }
  188. bool WindowCycleList::IsTabSliderFocused() {
  189. DCHECK(cycle_view_);
  190. return cycle_view_->IsTabSliderFocused();
  191. }
  192. bool WindowCycleList::IsEventInCycleView(const ui::LocatedEvent* event) {
  193. return cycle_view_ &&
  194. cycle_view_->GetBoundsInScreen().Contains(ConvertEventToScreen(event));
  195. }
  196. aura::Window* WindowCycleList::GetWindowAtPoint(const ui::LocatedEvent* event) {
  197. return cycle_view_
  198. ? cycle_view_->GetWindowAtPoint(ConvertEventToScreen(event))
  199. : nullptr;
  200. }
  201. bool WindowCycleList::IsEventInTabSliderContainer(
  202. const ui::LocatedEvent* event) {
  203. return cycle_view_ &&
  204. cycle_view_->IsEventInTabSliderContainer(ConvertEventToScreen(event));
  205. }
  206. bool WindowCycleList::ShouldShowUi() {
  207. // Show alt-tab when there are at least two windows to pick from alt-tab, or
  208. // when there is at least a window to switch to by switching to the different
  209. // mode.
  210. if (!Shell::Get()
  211. ->window_cycle_controller()
  212. ->IsInteractiveAltTabModeAllowed()) {
  213. return windows_.size() > 1u;
  214. }
  215. int total_window_in_all_desks = GetNumberOfWindowsAllDesks();
  216. return windows_.size() > 1u ||
  217. (windows_.size() <= 1u &&
  218. static_cast<size_t>(total_window_in_all_desks) > windows_.size());
  219. }
  220. void WindowCycleList::OnModePrefsChanged() {
  221. if (cycle_view_)
  222. cycle_view_->OnModePrefsChanged();
  223. }
  224. // static
  225. void WindowCycleList::SetDisableInitialDelayForTesting(bool disabled) {
  226. g_disable_initial_delay = disabled;
  227. }
  228. void WindowCycleList::OnWindowDestroying(aura::Window* window) {
  229. window->RemoveObserver(this);
  230. WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
  231. // TODO(oshima): Change this back to DCHECK once crbug.com/483491 is fixed.
  232. CHECK(i != windows_.end());
  233. int removed_index = static_cast<int>(i - windows_.begin());
  234. windows_.erase(i);
  235. if (current_index_ > removed_index ||
  236. current_index_ == static_cast<int>(windows_.size())) {
  237. current_index_--;
  238. }
  239. // Reset |active_window_before_window_cycle_| to avoid a dangling pointer.
  240. if (window == active_window_before_window_cycle_)
  241. active_window_before_window_cycle_ = nullptr;
  242. if (cycle_view_) {
  243. auto* new_target_window =
  244. windows_.empty() ? nullptr : windows_[current_index_];
  245. cycle_view_->HandleWindowDestruction(window, new_target_window);
  246. if (windows_.empty()) {
  247. // This deletes us.
  248. Shell::Get()->window_cycle_controller()->CancelCycling();
  249. return;
  250. }
  251. }
  252. }
  253. void WindowCycleList::OnDisplayMetricsChanged(const display::Display& display,
  254. uint32_t changed_metrics) {
  255. if (cycle_ui_widget_ &&
  256. display.id() ==
  257. display::Screen::GetScreen()
  258. ->GetDisplayNearestWindow(cycle_ui_widget_->GetNativeWindow())
  259. .id() &&
  260. (changed_metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION))) {
  261. Shell::Get()->window_cycle_controller()->CancelCycling();
  262. // |this| is deleted.
  263. return;
  264. }
  265. }
  266. void WindowCycleList::RemoveAllWindows() {
  267. for (auto* window : windows_) {
  268. window->RemoveObserver(this);
  269. if (cycle_view_)
  270. cycle_view_->HandleWindowDestruction(window, nullptr);
  271. }
  272. windows_.clear();
  273. current_index_ = 0;
  274. window_selected_ = false;
  275. }
  276. void WindowCycleList::InitWindowCycleView() {
  277. if (cycle_view_)
  278. return;
  279. aura::Window* root_window = GetRootWindowForCycleView();
  280. // Close the system quick settings tray before creating the cycle view.
  281. UnifiedSystemTray* tray = RootWindowController::ForWindow(root_window)
  282. ->GetStatusAreaWidget()
  283. ->unified_system_tray();
  284. if (tray->IsBubbleShown())
  285. tray->CloseBubble();
  286. cycle_view_ = new WindowCycleView(root_window, windows_);
  287. const bool is_interactive_alt_tab_mode_allowed =
  288. Shell::Get()->window_cycle_controller()->IsInteractiveAltTabModeAllowed();
  289. DCHECK(!windows_.empty() || is_interactive_alt_tab_mode_allowed);
  290. // Only set target window and scroll to the window when alt-tab is not empty.
  291. if (!windows_.empty()) {
  292. DCHECK(static_cast<int>(windows_.size()) > current_index_);
  293. cycle_view_->SetTargetWindow(windows_[current_index_]);
  294. cycle_view_->ScrollToWindow(windows_[current_index_]);
  295. }
  296. // We need to activate the widget if ChromeVox is enabled as ChromeVox
  297. // relies on activation.
  298. const bool spoken_feedback_enabled =
  299. Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
  300. views::Widget* widget = new views::Widget();
  301. views::Widget::InitParams params;
  302. params.delegate = cycle_view_;
  303. params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
  304. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  305. params.layer_type = ui::LAYER_NOT_DRAWN;
  306. // Don't let the alt-tab cycler be activatable. This lets the currently
  307. // activated window continue to be in the foreground. This may affect
  308. // things such as video automatically pausing/playing.
  309. if (!spoken_feedback_enabled)
  310. params.activatable = views::Widget::InitParams::Activatable::kNo;
  311. params.accept_events = true;
  312. params.name = "WindowCycleList (Alt+Tab)";
  313. // TODO(estade): make sure nothing untoward happens when the lock screen
  314. // or a system modal dialog is shown.
  315. params.parent = root_window->GetChildById(kShellWindowId_OverlayContainer);
  316. params.bounds = cycle_view_->GetTargetBounds();
  317. widget->Init(std::move(params));
  318. widget->Show();
  319. cycle_view_->FadeInLayer();
  320. cycle_ui_widget_ = widget;
  321. // Since this window is not activated, grab events.
  322. if (!spoken_feedback_enabled) {
  323. window_targeter_ = std::make_unique<aura::ScopedWindowTargeter>(
  324. widget->GetNativeWindow()->GetRootWindow(),
  325. std::make_unique<CustomWindowTargeter>(widget->GetNativeWindow()));
  326. }
  327. // Close the app list, if it's open in clamshell mode.
  328. if (!Shell::Get()->tablet_mode_controller()->InTabletMode())
  329. Shell::Get()->app_list_controller()->DismissAppList();
  330. Shell::Get()->frame_throttling_controller()->StartThrottling(windows_);
  331. }
  332. void WindowCycleList::SelectWindow(aura::Window* window) {
  333. // If the list has only one window, the window can be selected twice (in
  334. // Scroll() and the destructor). This causes ARC PIP windows to be restored
  335. // twice, which leads to a wrong window state.
  336. if (window_selected_)
  337. return;
  338. if (window->GetProperty(kPipOriginalWindowKey)) {
  339. window_util::ExpandArcPipWindow();
  340. } else {
  341. window->Show();
  342. WindowState::Get(window)->Activate();
  343. }
  344. window_selected_ = true;
  345. }
  346. void WindowCycleList::Scroll(int offset) {
  347. if (windows_.size() == 1)
  348. SelectWindow(windows_[0]);
  349. if (!ShouldShowUi()) {
  350. // When there is only one window, we should give feedback to the user. If
  351. // the window is minimized, we should also show it.
  352. if (windows_.size() == 1)
  353. ::wm::AnimateWindow(windows_[0], ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
  354. return;
  355. }
  356. DCHECK(static_cast<size_t>(current_index_) < windows_.size());
  357. current_index_ = GetOffsettedWindowIndex(offset);
  358. if (current_index_ > 1)
  359. InitWindowCycleView();
  360. // The windows should not shift position when selecting when there's enough
  361. // room to display all windows.
  362. if (cycle_view_ && cycle_view_->CalculatePreferredSize().width() ==
  363. cycle_view_->CalculateMaxWidth()) {
  364. cycle_view_->ScrollToWindow(windows_[current_index_]);
  365. }
  366. }
  367. int WindowCycleList::GetOffsettedWindowIndex(int offset) const {
  368. DCHECK(!windows_.empty());
  369. const int offsetted_index =
  370. (current_index_ + offset + windows_.size()) % windows_.size();
  371. DCHECK(windows_[offsetted_index]);
  372. return offsetted_index;
  373. }
  374. int WindowCycleList::GetIndexOfWindow(aura::Window* window) const {
  375. auto target_window = std::find(windows_.begin(), windows_.end(), window);
  376. DCHECK(target_window != windows_.end());
  377. return std::distance(windows_.begin(), target_window);
  378. }
  379. int WindowCycleList::GetNumberOfWindowsAllDesks() const {
  380. // If alt-tab mode is not available, the alt-tab defaults to all-desks mode
  381. // and can obtain the number of all windows easily from `windows_.size()`.
  382. DCHECK(Shell::Get()
  383. ->window_cycle_controller()
  384. ->IsInteractiveAltTabModeAllowed());
  385. return Shell::Get()
  386. ->mru_window_tracker()
  387. ->BuildWindowForCycleWithPipList(kAllDesks)
  388. .size();
  389. }
  390. } // namespace ash