blob: 93bcbf3238c97a6716a1cd9a038b6b0b5532891d [file] [log] [blame]
James Cookb0bf8e82017-04-09 17:01:441// Copyright 2016 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/shelf/shelf_controller.h"
6
Michael Wasserman39e4fe42017-08-08 23:49:427#include "ash/public/cpp/ash_pref_names.h"
msw19b30c2c2017-06-01 03:21:408#include "ash/public/cpp/config.h"
9#include "ash/public/cpp/remote_shelf_item_delegate.h"
Michael Wasserman39e4fe42017-08-08 23:49:4210#include "ash/public/cpp/shelf_prefs.h"
James Cookb0bf8e82017-04-09 17:01:4411#include "ash/root_window_controller.h"
jamescook788b4fc2017-05-18 16:16:0612#include "ash/session/session_controller.h"
James Cookb0bf8e82017-04-09 17:01:4413#include "ash/shelf/app_list_shelf_item_delegate.h"
James Cook840177e2017-05-25 02:20:0114#include "ash/shelf/shelf.h"
James Cookb0bf8e82017-04-09 17:01:4415#include "ash/shell.h"
msw109806d2017-06-02 20:11:5716#include "ash/strings/grit/ash_strings.h"
msw19b30c2c2017-06-01 03:21:4017#include "base/auto_reset.h"
James Cookb0bf8e82017-04-09 17:01:4418#include "base/strings/utf_string_conversions.h"
Michael Wasserman39e4fe42017-08-08 23:49:4219#include "components/pref_registry/pref_registry_syncable.h"
20#include "components/prefs/pref_change_registrar.h"
21#include "components/prefs/pref_registry_simple.h"
22#include "components/prefs/pref_service.h"
msw109806d2017-06-02 20:11:5723#include "ui/base/l10n/l10n_util.h"
James Cookb0bf8e82017-04-09 17:01:4424#include "ui/base/models/simple_menu_model.h"
James Cookb0bf8e82017-04-09 17:01:4425#include "ui/display/display.h"
26#include "ui/display/screen.h"
James Cookb0bf8e82017-04-09 17:01:4427
28namespace ash {
29
30namespace {
31
James Cook840177e2017-05-25 02:20:0132// Returns the Shelf instance for the display with the given |display_id|.
33Shelf* GetShelfForDisplay(int64_t display_id) {
James Cookb0bf8e82017-04-09 17:01:4434 // The controller may be null for invalid ids or for displays being removed.
35 RootWindowController* root_window_controller =
36 Shell::GetRootWindowControllerWithDisplayId(display_id);
jamescookaa9f49f2017-05-30 20:48:4537 return root_window_controller ? root_window_controller->shelf() : nullptr;
James Cookb0bf8e82017-04-09 17:01:4438}
39
Michael Wasserman39e4fe42017-08-08 23:49:4240// Set each Shelf's auto-hide behavior from the per-display pref.
41void SetShelfAutoHideFromPrefs() {
42 // TODO(jamescook): The session state check should not be necessary, but
43 // otherwise this wrongly tries to set the alignment on a secondary display
44 // during login before the ShelfLockingManager is created.
45 SessionController* session_controller = Shell::Get()->session_controller();
46 PrefService* prefs = Shell::Get()->GetActiveUserPrefService();
47 if (!prefs || !session_controller->IsActiveUserSessionStarted())
48 return;
49
50 for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
51 auto value = GetShelfAutoHideBehaviorPref(prefs, display.id());
52 // Don't show the shelf in app mode.
53 if (session_controller->IsRunningInAppMode())
54 value = SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
55 Shelf* shelf = GetShelfForDisplay(display.id());
56 if (shelf)
57 shelf->SetAutoHideBehavior(value);
58 }
59}
60
61// Set each Shelf's alignment from the per-display pref.
62void SetShelfAlignmentFromPrefs() {
63 // TODO(jamescook): The session state check should not be necessary, but
64 // otherwise this wrongly tries to set the alignment on a secondary display
65 // during login before the ShelfLockingManager is created.
66 SessionController* session_controller = Shell::Get()->session_controller();
67 PrefService* prefs = Shell::Get()->GetActiveUserPrefService();
68 if (!prefs || !session_controller->IsActiveUserSessionStarted())
69 return;
70
71 for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
72 auto value = GetShelfAlignmentPref(prefs, display.id());
73 Shelf* shelf = GetShelfForDisplay(display.id());
74 if (shelf)
75 shelf->SetAlignment(value);
76 }
77}
78
79// Set each Shelf's auto-hide behavior and alignment from the per-display prefs.
80void SetShelfBehaviorsFromPrefs() {
81 SetShelfAutoHideFromPrefs();
82 SetShelfAlignmentFromPrefs();
83}
84
James Cookb0bf8e82017-04-09 17:01:4485} // namespace
86
87ShelfController::ShelfController() {
msw109806d2017-06-02 20:11:5788 // Set the delegate and title string for the app list item.
msw19b30c2c2017-06-01 03:21:4089 model_.SetShelfItemDelegate(ShelfID(kAppListId),
90 base::MakeUnique<AppListShelfItemDelegate>());
msw109806d2017-06-02 20:11:5791 DCHECK_EQ(0, model_.ItemIndexByID(ShelfID(kAppListId)));
92 ShelfItem item = model_.items()[0];
93 item.title = l10n_util::GetStringUTF16(IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE);
94 model_.Set(0, item);
95
msw19b30c2c2017-06-01 03:21:4096 model_.AddObserver(this);
Michael Wasserman39e4fe42017-08-08 23:49:4297 Shell::Get()->AddShellObserver(this);
98 Shell::Get()->window_tree_host_manager()->AddObserver(this);
James Cookb0bf8e82017-04-09 17:01:4499}
100
msw19b30c2c2017-06-01 03:21:40101ShelfController::~ShelfController() {
Michael Wasserman39e4fe42017-08-08 23:49:42102 Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
103 Shell::Get()->RemoveShellObserver(this);
msw19b30c2c2017-06-01 03:21:40104 model_.RemoveObserver(this);
105}
James Cookb0bf8e82017-04-09 17:01:44106
Michael Wasserman39e4fe42017-08-08 23:49:42107// static
108void ShelfController::RegisterProfilePrefs(PrefRegistrySimple* registry) {
109 // These prefs are marked PUBLIC for use by Chrome, they're currently only
110 // needed for ChromeLauncherController::ShelfBoundsChangesProbablyWithUser
111 // and ChromeLauncherPrefsObserver. See the pref names definitions for an
112 // explanation of the synced, local, and per-display behavior of these prefs.
113 registry->RegisterStringPref(
114 prefs::kShelfAutoHideBehavior, kShelfAutoHideBehaviorNever,
115 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC);
116 registry->RegisterStringPref(prefs::kShelfAutoHideBehaviorLocal,
117 std::string(), PrefRegistry::PUBLIC);
118 registry->RegisterStringPref(
119 prefs::kShelfAlignment, kShelfAlignmentBottom,
120 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC);
121 registry->RegisterStringPref(prefs::kShelfAlignmentLocal, std::string(),
122 PrefRegistry::PUBLIC);
123 registry->RegisterDictionaryPref(prefs::kShelfPreferences,
124 PrefRegistry::PUBLIC);
125}
126
James Cookb0bf8e82017-04-09 17:01:44127void ShelfController::BindRequest(mojom::ShelfControllerRequest request) {
128 bindings_.AddBinding(this, std::move(request));
129}
130
James Cookb0bf8e82017-04-09 17:01:44131void ShelfController::AddObserver(
132 mojom::ShelfObserverAssociatedPtrInfo observer) {
133 mojom::ShelfObserverAssociatedPtr observer_ptr;
134 observer_ptr.Bind(std::move(observer));
msw19b30c2c2017-06-01 03:21:40135
136 if (Shell::GetAshConfig() == Config::MASH) {
137 // Mash synchronizes two ShelfModel instances, owned by Ash and Chrome.
138 // Notify Chrome of existing ShelfModel items created by Ash.
139 for (int i = 0; i < model_.item_count(); ++i) {
140 const ShelfItem& item = model_.items()[i];
141 observer_ptr->OnShelfItemAdded(i, item);
142 ShelfItemDelegate* delegate = model_.GetShelfItemDelegate(item.id);
Michael Wasserman78b6f3e2017-07-20 19:51:20143 if (delegate) {
144 observer_ptr->OnShelfItemDelegateChanged(
145 item.id, delegate->CreateInterfacePtrAndBind());
146 }
msw19b30c2c2017-06-01 03:21:40147 }
148 }
149
James Cookb0bf8e82017-04-09 17:01:44150 observers_.AddPtr(std::move(observer_ptr));
151}
152
msw19b30c2c2017-06-01 03:21:40153void ShelfController::AddShelfItem(int32_t index, const ShelfItem& item) {
msw70ac45f2017-06-05 02:02:45154 DCHECK_EQ(Shell::GetAshConfig(), Config::MASH) << " Unexpected model sync";
155 DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
msw19b30c2c2017-06-01 03:21:40156 index = index < 0 ? model_.item_count() : index;
Julien Brianceau293917a82017-08-01 17:32:59157 DCHECK_GT(index, 0) << " Items can not precede the AppList";
msw70ac45f2017-06-05 02:02:45158 DCHECK_LE(index, model_.item_count()) << " Index out of bounds";
msw19b30c2c2017-06-01 03:21:40159 index = std::min(std::max(index, 1), model_.item_count());
160 base::AutoReset<bool> reset(&applying_remote_shelf_model_changes_, true);
161 model_.AddAt(index, item);
James Cookb0bf8e82017-04-09 17:01:44162}
163
msw19b30c2c2017-06-01 03:21:40164void ShelfController::RemoveShelfItem(const ShelfID& id) {
msw70ac45f2017-06-05 02:02:45165 DCHECK_EQ(Shell::GetAshConfig(), Config::MASH) << " Unexpected model sync";
166 DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
msw19b30c2c2017-06-01 03:21:40167 const int index = model_.ItemIndexByID(id);
msw70ac45f2017-06-05 02:02:45168 DCHECK_GE(index, 0) << " No item found with the id: " << id;
169 DCHECK_NE(index, 0) << " The AppList shelf item cannot be removed";
msw19b30c2c2017-06-01 03:21:40170 if (index <= 0)
171 return;
172 base::AutoReset<bool> reset(&applying_remote_shelf_model_changes_, true);
173 model_.RemoveItemAt(index);
James Cookb0bf8e82017-04-09 17:01:44174}
175
msw19b30c2c2017-06-01 03:21:40176void ShelfController::MoveShelfItem(const ShelfID& id, int32_t index) {
msw70ac45f2017-06-05 02:02:45177 DCHECK_EQ(Shell::GetAshConfig(), Config::MASH) << " Unexpected model sync";
178 DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
msw19b30c2c2017-06-01 03:21:40179 const int current_index = model_.ItemIndexByID(id);
msw70ac45f2017-06-05 02:02:45180 DCHECK_GE(current_index, 0) << " No item found with the id: " << id;
181 DCHECK_NE(current_index, 0) << " The AppList shelf item cannot be moved";
msw19b30c2c2017-06-01 03:21:40182 if (current_index <= 0)
183 return;
Julien Brianceau293917a82017-08-01 17:32:59184 DCHECK_GT(index, 0) << " Items can not precede the AppList";
msw70ac45f2017-06-05 02:02:45185 DCHECK_LT(index, model_.item_count()) << " Index out of bounds";
msw19b30c2c2017-06-01 03:21:40186 index = std::min(std::max(index, 1), model_.item_count() - 1);
msw70ac45f2017-06-05 02:02:45187 DCHECK_NE(current_index, index) << " The item is already at the given index";
msw19b30c2c2017-06-01 03:21:40188 if (current_index == index)
189 return;
190 base::AutoReset<bool> reset(&applying_remote_shelf_model_changes_, true);
191 model_.Move(current_index, index);
192}
193
194void ShelfController::UpdateShelfItem(const ShelfItem& item) {
msw70ac45f2017-06-05 02:02:45195 DCHECK_EQ(Shell::GetAshConfig(), Config::MASH) << " Unexpected model sync";
196 DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
msw19b30c2c2017-06-01 03:21:40197 const int index = model_.ItemIndexByID(item.id);
msw70ac45f2017-06-05 02:02:45198 DCHECK_GE(index, 0) << " No item found with the id: " << item.id;
msw19b30c2c2017-06-01 03:21:40199 if (index < 0)
200 return;
201 base::AutoReset<bool> reset(&applying_remote_shelf_model_changes_, true);
202 model_.Set(index, item);
203}
204
205void ShelfController::SetShelfItemDelegate(
206 const ShelfID& id,
207 mojom::ShelfItemDelegatePtr delegate) {
msw70ac45f2017-06-05 02:02:45208 DCHECK_EQ(Shell::GetAshConfig(), Config::MASH) << " Unexpected model sync";
209 DCHECK(!applying_remote_shelf_model_changes_) << " Unexpected model change";
msw19b30c2c2017-06-01 03:21:40210 base::AutoReset<bool> reset(&applying_remote_shelf_model_changes_, true);
211 if (delegate.is_bound())
212 model_.SetShelfItemDelegate(
213 id, base::MakeUnique<RemoteShelfItemDelegate>(id, std::move(delegate)));
214 else
215 model_.SetShelfItemDelegate(id, nullptr);
216}
217
218void ShelfController::ShelfItemAdded(int index) {
219 if (applying_remote_shelf_model_changes_ ||
220 Shell::GetAshConfig() != Config::MASH) {
221 return;
222 }
223
224 const ShelfItem& item = model_.items()[index];
225 observers_.ForAllPtrs([index, item](mojom::ShelfObserver* observer) {
226 observer->OnShelfItemAdded(index, item);
227 });
228}
229
230void ShelfController::ShelfItemRemoved(int index, const ShelfItem& old_item) {
231 if (applying_remote_shelf_model_changes_ ||
232 Shell::GetAshConfig() != Config::MASH) {
233 return;
234 }
235
236 observers_.ForAllPtrs([old_item](mojom::ShelfObserver* observer) {
237 observer->OnShelfItemRemoved(old_item.id);
238 });
239}
240
241void ShelfController::ShelfItemMoved(int start_index, int target_index) {
242 if (applying_remote_shelf_model_changes_ ||
243 Shell::GetAshConfig() != Config::MASH) {
244 return;
245 }
246
247 const ShelfItem& item = model_.items()[target_index];
248 observers_.ForAllPtrs([item, target_index](mojom::ShelfObserver* observer) {
249 observer->OnShelfItemMoved(item.id, target_index);
250 });
251}
252
253void ShelfController::ShelfItemChanged(int index, const ShelfItem& old_item) {
254 if (applying_remote_shelf_model_changes_ ||
255 Shell::GetAshConfig() != Config::MASH) {
256 return;
257 }
258
259 const ShelfItem& item = model_.items()[index];
260 observers_.ForAllPtrs([item](mojom::ShelfObserver* observer) {
261 observer->OnShelfItemUpdated(item);
262 });
263}
264
265void ShelfController::ShelfItemDelegateChanged(const ShelfID& id,
266 ShelfItemDelegate* delegate) {
267 if (applying_remote_shelf_model_changes_ ||
268 Shell::GetAshConfig() != Config::MASH) {
269 return;
270 }
271
272 observers_.ForAllPtrs([id, delegate](mojom::ShelfObserver* observer) {
273 observer->OnShelfItemDelegateChanged(
274 id, delegate ? delegate->CreateInterfacePtrAndBind()
275 : mojom::ShelfItemDelegatePtr());
276 });
James Cookb0bf8e82017-04-09 17:01:44277}
278
Michael Wasserman39e4fe42017-08-08 23:49:42279void ShelfController::OnActiveUserPrefServiceChanged(
280 PrefService* pref_service) {
281 pref_change_registrar_.reset();
282 if (!pref_service) // Null during startup, user switch and tests.
283 return;
284 SetShelfBehaviorsFromPrefs();
285 pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
286 pref_change_registrar_->Init(pref_service);
287 pref_change_registrar_->Add(prefs::kShelfAlignmentLocal,
288 base::Bind(&SetShelfAlignmentFromPrefs));
289 pref_change_registrar_->Add(prefs::kShelfAutoHideBehaviorLocal,
290 base::Bind(&SetShelfAutoHideFromPrefs));
291 pref_change_registrar_->Add(prefs::kShelfPreferences,
292 base::Bind(&SetShelfBehaviorsFromPrefs));
293}
294
295void ShelfController::OnDisplayConfigurationChanged() {
296 // Set/init the shelf behaviors from preferences, in case a display was added.
297 SetShelfBehaviorsFromPrefs();
298}
299
300void ShelfController::OnWindowTreeHostReusedForDisplay(
301 AshWindowTreeHost* window_tree_host,
302 const display::Display& display) {
303 // See comment in OnWindowTreeHostsSwappedDisplays().
304 SetShelfBehaviorsFromPrefs();
305}
306
307void ShelfController::OnWindowTreeHostsSwappedDisplays(
308 AshWindowTreeHost* host1,
309 AshWindowTreeHost* host2) {
310 // The display ids for existing shelf instances may have changed, so update
311 // the alignment and auto-hide state from prefs. See http://crbug.com/748291
312 SetShelfBehaviorsFromPrefs();
313}
314
James Cookb0bf8e82017-04-09 17:01:44315} // namespace ash