1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
ash / public / cpp / shelf_model.h [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_PUBLIC_CPP_SHELF_MODEL_H_
#define ASH_PUBLIC_CPP_SHELF_MODEL_H_
#include <map>
#include <memory>
#include "ash/public/cpp/ash_public_export.h"
#include "ash/public/cpp/shelf_item.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
class AppWindowShelfItemController;
namespace ash {
class ShelfItemDelegate;
class ShelfModelObserver;
// An id for the AppList item, which is added in the ShelfModel constructor.
// Generated as crx_file::id_util::GenerateId("org.chromium.applist")
ASH_PUBLIC_EXPORT extern const char kAppListId[];
// An id for the BackButton item, which is added in the ShelfModel constructor.
// Generated as crx_file::id_util::GenerateId("org.chromium.backbutton")
ASH_PUBLIC_EXPORT extern const char kBackButtonId[];
// Model used for shelf items. Owns ShelfItemDelegates but does not create them.
class ASH_PUBLIC_EXPORT ShelfModel {
public:
// Get or set a weak pointer to the singleton ShelfModel instance, not owned.
static ShelfModel* Get();
static void SetInstance(ShelfModel* shelf_model);
// Used to mark the current shelf model mutation as user-triggered, while
// the instance of this class is in scope.
class ScopedUserTriggeredMutation {
public:
explicit ScopedUserTriggeredMutation(ShelfModel* model) : model_(model) {
model_->current_mutation_is_user_triggered_++;
}
~ScopedUserTriggeredMutation() {
model_->current_mutation_is_user_triggered_--;
DCHECK_GE(model_->current_mutation_is_user_triggered_, 0);
}
private:
raw_ptr<ShelfModel> model_ = nullptr;
};
// Some classes in ash have the ability to insert an item into the ShelfModel,
// but with no knowledge of the item beyond an |app_id|. This delegate creates
// an explicit mechanism for those classes to fetch both a ShelfItem and a
// ShelfItemDelegate.
//
// If we were designing the architecture from scratch, we probably would not
// need this class at all. The point of this class is to take a previous
// implicit dependency from //ash on //chrome and make it explicit.
class ShelfItemFactory {
public:
// Creates a shelf item for an app..
virtual std::unique_ptr<ShelfItem> CreateShelfItemForApp(
const ash::ShelfID& app_id,
ash::ShelfItemStatus status,
ash::ShelfItemType shelf_item_type,
const std::u16string& title) = 0;
// Creates a shelf item delegate for a given `app_id`.
virtual std::unique_ptr<ShelfItemDelegate> CreateShelfItemDelegateForAppId(
const std::string& app_id) = 0;
};
ShelfModel();
ShelfModel(const ShelfModel&) = delete;
ShelfModel& operator=(const ShelfModel&) = delete;
~ShelfModel();
// Adds an item to the shelf, using the default factory to construct a
// delegate. If a delegate cannot be constructed for this type of app, then no
// item will be added.
// Prefer to use AddItem directly when the delegate can be easily created.
void AddAndPinAppWithFactoryConstructedDelegate(const std::string& app_id);
// This function can only be called with |app_id| is already present in the
// shelf. Changes the ShelfItem state to be pinned. This method has no effect
// if the item is already pinned.
void PinExistingItemWithID(const std::string& app_id);
// Checks if the app with |app_id_| is pinned to the shelf.
bool IsAppPinned(const std::string& app_id) const;
// Returns whether the app specified by `app_id` is allowed to be set with the
// target pin state. If `target_pin` is true, the target is to pin the item;
// otherwise, the target is to unpin the item.
// Returns true if the app's current pin state matches the target state, even
// if the app pin state is not modifiable (e.g. due to policy).
bool AllowedToSetAppPinState(const std::string& app_id,
bool target_pin) const;
// Unpins app item with |app_id|.
void UnpinAppWithID(const std::string& app_id);
// Cleans up the ShelfItemDelegates.
void DestroyItemDelegates();
// Adds a new item to the model. Returns the resulting index.
int Add(const ShelfItem& item, std::unique_ptr<ShelfItemDelegate> delegate);
// Adds the item. |index| is the requested insertion index, which may be
// modified to meet type-based ordering. Returns the actual insertion index.
int AddAt(int index,
const ShelfItem& item,
std::unique_ptr<ShelfItemDelegate> delegate);
// Removes the item at |index|.
void RemoveItemAt(int index);
// Removes the item with id |shelf_id| and passes ownership of its
// ShelfItemDelegate to the caller. This is useful if you want to remove an
// item from the shelf temporarily and be able to restore its behavior later.
std::unique_ptr<ShelfItemDelegate> RemoveItemAndTakeShelfItemDelegate(
const ShelfID& shelf_id);
// Returns whether the item with the given index can be swapped with the
// next (or previous) item. Example cases when a swap cannot happen are:
// trying to swap the first item with the previous one, trying to swap
// the last item with the next one, trying to swap a pinned item with an
// unpinned item.
bool CanSwap(int index, bool with_next) const;
// Swaps the item at the given index with the next one if |with_next| is
// true, or with the previous one if |with_next| is false. Returns true
// if the requested swap has happened, and false otherwise.
bool Swap(int index, bool with_next);
// Moves the item at |index| to |target_index|. |target_index| is in terms
// of the model *after* the item at |index| is removed.
void Move(int index, int target_index);
// Resets the item at the specified index. The item's id should not change.
void Set(int index, const ShelfItem& item);
// Updates the items' |is_on_active_desk| from the given vector
// |items_desk_updates|. Items whose indices are not included in
// |items_desk_updates| will remain unchanged.
struct ItemDeskUpdate {
// The index of the item being updated.
int index = -1;
// The new value of the item's |ShelfItem::is_on_active_desk|.
bool is_on_active_desk = false;
};
void UpdateItemsForDeskChange(
const std::vector<ItemDeskUpdate>& items_desk_updates);
// Returns the ID of the currently active item, or an empty ShelfID if
// nothing is currently active.
const ShelfID& active_shelf_id() const { return active_shelf_id_; }
// Returns whether the mutation that is currently being made in the model
// was user-triggered.
bool is_current_mutation_user_triggered() const {
return current_mutation_is_user_triggered_ > 0;
}
// Sets |shelf_id| to be the newly active shelf item.
void SetActiveShelfID(const ShelfID& shelf_id);
// Notifies observers that the status of the item corresponding to |id|
// has changed.
void OnItemStatusChanged(const ShelfID& id);
// Notifies observers that an item has been dragged off the shelf (it is still
// being dragged).
void OnItemRippedOff();
// Notifies observers that an item that was dragged off the shelf has been
// dragged back onto the shelf (it is still being dragged).
void OnItemReturnedFromRipOff(int index);
// Update the ShelfItem with |app_id| to set whether the item currently has a
// notification.
void UpdateItemNotification(const std::string& app_id, bool has_badge);
// Returns the index of the item with id |shelf_id|, or -1 if none exists.
int ItemIndexByID(const ShelfID& shelf_id) const;
// Returns the |index| of the item matching |type| in |items_|.
// Returns -1 if the matching item is not found.
int GetItemIndexForType(ShelfItemType type);
// Returns the index of the first running application or the index where the
// first running application would go if there are no running (non pinned)
// applications yet.
int FirstRunningAppIndex() const;
// Returns a pointer of ShelfItem with the given |shelf_id| in this model.
// Or, nullptr if not found.
const ShelfItem* ItemByID(const ShelfID& shelf_id) const;
// Returns the index of the matching ShelfItem or -1 if the |app_id| doesn't
// match a ShelfItem.
int ItemIndexByAppID(const std::string& app_id) const;
const ShelfItems& items() const { return items_; }
int item_count() const { return static_cast<int>(items_.size()); }
// Sets |item_delegate| for the given |shelf_id| and takes ownership.
void ReplaceShelfItemDelegate(
const ShelfID& shelf_id,
std::unique_ptr<ShelfItemDelegate> item_delegate);
// Returns ShelfItemDelegate for |shelf_id|, or nullptr if none exists.
ShelfItemDelegate* GetShelfItemDelegate(const ShelfID& shelf_id) const;
// Sets the ShelfItemFactory.
void SetShelfItemFactory(ShelfItemFactory* factory);
// Returns AppWindowShelfItemController for |shelf_id|, or nullptr if none
// exists.
AppWindowShelfItemController* GetAppWindowShelfItemController(
const ShelfID& shelf_id);
void AddObserver(ShelfModelObserver* observer);
void RemoveObserver(ShelfModelObserver* observer);
private:
// Makes sure |index| is in line with the type-based order of items. If that
// is not the case, adjusts index by shifting it to the valid range and
// returns the new value.
int ValidateInsertionIndex(ShelfItemType type, int index) const;
ShelfItems items_;
// This pointer must outlive this class.
raw_ptr<ShelfItemFactory, DanglingUntriaged> shelf_item_factory_ = nullptr;
// The shelf ID of the currently active shelf item, or an empty ID if
// nothing is active.
ShelfID active_shelf_id_;
// A counter to determine whether any mutation currently in progress in
// the model is the result of a manual user intervention. If a shelf item
// is added once an app has been installed, it is not considered a direct
// user interaction.
int current_mutation_is_user_triggered_ = 0;
base::ObserverList<ShelfModelObserver>::UncheckedAndDanglingUntriaged
observers_;
std::map<ShelfID, std::unique_ptr<ShelfItemDelegate>>
id_to_item_delegate_map_;
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_SHELF_MODEL_H_