1 // Copyright 2019 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
5 #include "ash/wm/overview/overview_highlight_controller.h"
6
7 #include "ash/magnifier/docked_magnifier_controller_impl.h"
8 #include "ash/magnifier/magnification_controller.h"
9 #include "ash/shell.h"
10 #include "ash/wm/desks/desk_mini_view.h"
11 #include "ash/wm/desks/desk_name_view.h"
12 #include "ash/wm/desks/desks_bar_view.h"
13 #include "ash/wm/desks/desks_util.h"
14 #include "ash/wm/desks/new_desk_button.h"
15 #include "ash/wm/overview/overview_grid.h"
16 #include "ash/wm/overview/overview_item.h"
17 #include "ash/wm/overview/overview_item_view.h"
18 #include "ash/wm/overview/overview_session.h"
19 #include "ash/wm/overview/overview_utils.h"
20 #include "ui/accessibility/ax_enums.mojom.h"
21 #include "ui/views/view.h"
22
23 namespace ash {
24
25 // -----------------------------------------------------------------------------
26 // OverviewHighlightController::OverviewHighlightableView
27
28 void OverviewHighlightController::OverviewHighlightableView::
SetHighlightVisibility(bool visible)29 SetHighlightVisibility(bool visible) {
30 if (visible == is_highlighted_)
31 return;
32
33 is_highlighted_ = visible;
34 if (is_highlighted_)
35 OnViewHighlighted();
36 else
37 OnViewUnhighlighted();
38 }
39
40 gfx::Point OverviewHighlightController::OverviewHighlightableView::
GetMagnifierFocusPointInScreen()41 GetMagnifierFocusPointInScreen() {
42 return GetView()->GetBoundsInScreen().CenterPoint();
43 }
44
45 // -----------------------------------------------------------------------------
46 // OverviewHighlightController::TestApi
47
TestApi(OverviewHighlightController * highlight_controller)48 OverviewHighlightController::TestApi::TestApi(
49 OverviewHighlightController* highlight_controller)
50 : highlight_controller_(highlight_controller) {}
51
52 OverviewHighlightController::TestApi::~TestApi() = default;
53
54 OverviewHighlightController::OverviewHighlightableView*
GetHighlightView() const55 OverviewHighlightController::TestApi::GetHighlightView() const {
56 return highlight_controller_->highlighted_view_;
57 }
58
59 // -----------------------------------------------------------------------------
60 // OverviewHighlightController
61
OverviewHighlightController(OverviewSession * overview_session)62 OverviewHighlightController::OverviewHighlightController(
63 OverviewSession* overview_session)
64 : overview_session_(overview_session) {}
65
66 OverviewHighlightController::~OverviewHighlightController() = default;
67
MoveHighlight(bool reverse)68 void OverviewHighlightController::MoveHighlight(bool reverse) {
69 const std::vector<OverviewHighlightableView*> traversable_views =
70 GetTraversableViews();
71 const int count = int{traversable_views.size()};
72
73 // |count| can be zero when there are no overview items and no desk views (eg.
74 // "No recent items" or PIP windows are shown but they aren't traversable).
75 if (count == 0)
76 return;
77
78 int index = 0;
79 if (!highlighted_view_) {
80 // Pick up where we left off if |deleted_index_| has a value.
81 if (deleted_index_) {
82 index = *deleted_index_ >= count ? count - 1 : *deleted_index_;
83 deleted_index_.reset();
84 } else if (reverse) {
85 index = count - 1;
86 }
87 } else {
88 auto it = std::find(traversable_views.begin(), traversable_views.end(),
89 highlighted_view_);
90 DCHECK(it != traversable_views.end());
91 const int current_index = std::distance(traversable_views.begin(), it);
92 DCHECK_GE(current_index, 0);
93 index = (((reverse ? -1 : 1) + current_index) + count) % count;
94 }
95
96 UpdateHighlight(traversable_views[index], reverse);
97 }
98
OnViewDestroyingOrDisabling(OverviewHighlightableView * view)99 void OverviewHighlightController::OnViewDestroyingOrDisabling(
100 OverviewHighlightableView* view) {
101 DCHECK(view);
102
103 // TODO(afakhry): Refactor this code.
104 const std::vector<OverviewHighlightableView*> traversable_views =
105 GetTraversableViews();
106 const auto it =
107 std::find(traversable_views.begin(), traversable_views.end(), view);
108 if (it == traversable_views.end())
109 return;
110
111 const int view_index = std::distance(traversable_views.begin(), it);
112 DCHECK_GE(view_index, 0);
113
114 if (view != highlighted_view_) {
115 if (!deleted_index_)
116 return;
117
118 // We need to update the |deleted_index_| in case the destroying view
119 // resides before a previously removed highlighted view in the highlight
120 // order.
121 if (view_index < *deleted_index_)
122 deleted_index_ = std::max(0, --(*deleted_index_));
123 return;
124 }
125
126 deleted_index_ = view_index;
127 highlighted_view_->SetHighlightVisibility(false);
128 highlighted_view_ = nullptr;
129 }
130
SetFocusHighlightVisibility(bool visible)131 void OverviewHighlightController::SetFocusHighlightVisibility(bool visible) {
132 if (highlighted_view_)
133 highlighted_view_->SetHighlightVisibility(visible);
134 }
135
IsFocusHighlightVisible() const136 bool OverviewHighlightController::IsFocusHighlightVisible() const {
137 return highlighted_view_ && highlighted_view_->IsViewHighlighted();
138 }
139
MaybeActivateHighlightedView()140 bool OverviewHighlightController::MaybeActivateHighlightedView() {
141 if (!highlighted_view_)
142 return false;
143
144 highlighted_view_->MaybeActivateHighlightedView();
145 return true;
146 }
147
MaybeCloseHighlightedView()148 bool OverviewHighlightController::MaybeCloseHighlightedView() {
149 if (!highlighted_view_)
150 return false;
151
152 highlighted_view_->MaybeCloseHighlightedView();
153 return true;
154 }
155
GetHighlightedItem() const156 OverviewItem* OverviewHighlightController::GetHighlightedItem() const {
157 if (!highlighted_view_)
158 return nullptr;
159
160 for (auto& grid : overview_session_->grid_list()) {
161 for (auto& item : grid->window_list()) {
162 if (highlighted_view_->GetView() == item->overview_item_view())
163 return item.get();
164 }
165 }
166
167 return nullptr;
168 }
169
HideTabDragHighlight()170 void OverviewHighlightController::HideTabDragHighlight() {
171 if (tab_dragged_view_)
172 tab_dragged_view_->SetHighlightVisibility(false);
173 tab_dragged_view_ = nullptr;
174 }
175
ShowTabDragHighlight(OverviewHighlightableView * view)176 void OverviewHighlightController::ShowTabDragHighlight(
177 OverviewHighlightableView* view) {
178 if (tab_dragged_view_)
179 tab_dragged_view_->SetHighlightVisibility(false);
180 tab_dragged_view_ = view;
181 tab_dragged_view_->SetHighlightVisibility(true);
182 }
183
IsTabDragHighlightVisible() const184 bool OverviewHighlightController::IsTabDragHighlightVisible() const {
185 return !!tab_dragged_view_;
186 }
187
188 std::vector<OverviewHighlightController::OverviewHighlightableView*>
GetTraversableViews() const189 OverviewHighlightController::GetTraversableViews() const {
190 std::vector<OverviewHighlightableView*> traversable_views;
191 traversable_views.reserve(overview_session_->num_items() +
192 (desks_util::kMaxNumberOfDesks + 1) *
193 Shell::Get()->GetAllRootWindows().size());
194 for (auto& grid : overview_session_->grid_list()) {
195 auto* bar_view = grid->desks_bar_view();
196 if (bar_view) {
197 // The desk items are always traversable from left to right, even in RTL
198 // languages.
199 for (auto* mini_view : bar_view->mini_views()) {
200 traversable_views.push_back(mini_view);
201 traversable_views.push_back(mini_view->desk_name_view());
202 }
203
204 if (bar_view->new_desk_button()->GetEnabled())
205 traversable_views.push_back(bar_view->new_desk_button());
206 }
207
208 for (auto& item : grid->window_list())
209 traversable_views.push_back(item->overview_item_view());
210 }
211 return traversable_views;
212 }
213
UpdateHighlight(OverviewHighlightableView * view_to_be_highlighted,bool reverse)214 void OverviewHighlightController::UpdateHighlight(
215 OverviewHighlightableView* view_to_be_highlighted,
216 bool reverse) {
217 if (highlighted_view_ == view_to_be_highlighted)
218 return;
219
220 OverviewHighlightableView* previous_view = highlighted_view_;
221 highlighted_view_ = view_to_be_highlighted;
222
223 // Perform accessibility related tasks.
224 highlighted_view_->GetView()->NotifyAccessibilityEvent(
225 ax::mojom::Event::kSelection, true);
226 // Note that both magnifiers are mutually exclusive. The overview "focus"
227 // works differently from regular focusing so we need to update the magnifier
228 // manually here.
229 DockedMagnifierControllerImpl* docked_magnifier =
230 Shell::Get()->docked_magnifier_controller();
231 MagnificationController* fullscreen_magnifier =
232 Shell::Get()->magnification_controller();
233 const gfx::Point point_of_interest =
234 highlighted_view_->GetMagnifierFocusPointInScreen();
235 if (docked_magnifier->GetEnabled())
236 docked_magnifier->CenterOnPoint(point_of_interest);
237 else if (fullscreen_magnifier->IsEnabled())
238 fullscreen_magnifier->CenterOnPoint(point_of_interest);
239
240 if (previous_view)
241 previous_view->SetHighlightVisibility(false);
242 highlighted_view_->SetHighlightVisibility(true);
243 }
244
245 } // namespace ash
246