shelf_model.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. // Copyright 2013 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/public/cpp/shelf_model.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "ash/public/cpp/shelf_item_delegate.h"
  8. #include "ash/public/cpp/shelf_model_observer.h"
  9. #include "ash/public/cpp/shelf_types.h"
  10. namespace ash {
  11. namespace {
  12. static ShelfModel* g_shelf_model = nullptr;
  13. int ShelfItemTypeToWeight(ShelfItemType type) {
  14. switch (type) {
  15. case TYPE_PINNED_APP:
  16. case TYPE_BROWSER_SHORTCUT:
  17. return 1;
  18. case TYPE_APP:
  19. case TYPE_UNPINNED_BROWSER_SHORTCUT:
  20. return 2;
  21. case TYPE_DIALOG:
  22. return 3;
  23. case TYPE_UNDEFINED:
  24. NOTREACHED() << "ShelfItemType must be set";
  25. return -1;
  26. }
  27. NOTREACHED() << "Invalid type " << type;
  28. return 1;
  29. }
  30. bool CompareByWeight(const ShelfItem& a, const ShelfItem& b) {
  31. return ShelfItemTypeToWeight(a.type) < ShelfItemTypeToWeight(b.type);
  32. }
  33. } // namespace
  34. ShelfModel* ShelfModel::Get() {
  35. DCHECK(g_shelf_model);
  36. return g_shelf_model;
  37. }
  38. void ShelfModel::SetInstance(ShelfModel* shelf_model) {
  39. g_shelf_model = shelf_model;
  40. }
  41. ShelfModel::ShelfModel() = default;
  42. ShelfModel::~ShelfModel() = default;
  43. void ShelfModel::AddAndPinAppWithFactoryConstructedDelegate(
  44. const std::string& app_id) {
  45. DCHECK_LT(ItemIndexByAppID(app_id), 0);
  46. ShelfItem item;
  47. std::unique_ptr<ShelfItemDelegate> delegate;
  48. bool result =
  49. shelf_item_factory_->CreateShelfItemForAppId(app_id, &item, &delegate);
  50. if (!result)
  51. return;
  52. item.type = TYPE_PINNED_APP;
  53. Add(item, std::move(delegate));
  54. }
  55. void ShelfModel::PinExistingItemWithID(const std::string& app_id) {
  56. const int index = ItemIndexByAppID(app_id);
  57. DCHECK_GE(index, 0);
  58. if (IsAppPinned(app_id))
  59. return;
  60. ShelfItem item = items_[index];
  61. DCHECK_EQ(item.type, TYPE_APP);
  62. DCHECK(!item.pinned_by_policy);
  63. item.type = TYPE_PINNED_APP;
  64. Set(index, item);
  65. }
  66. bool ShelfModel::IsAppPinned(const std::string& app_id) const {
  67. const int index = ItemIndexByID(ShelfID(app_id));
  68. if (index < 0)
  69. return false;
  70. return IsPinnedShelfItemType(items_[index].type);
  71. }
  72. bool ShelfModel::AllowedToSetAppPinState(const std::string& app_id,
  73. bool target_pin) const {
  74. if (IsAppPinned(app_id) == target_pin)
  75. return true;
  76. const ShelfID shelf_id(app_id);
  77. const int index = ItemIndexByID(shelf_id);
  78. if (index < 0) {
  79. // Allow to pin an app which is not open.
  80. return !shelf_id.IsNull() && target_pin;
  81. }
  82. const ShelfItem& item = items_[index];
  83. if (item.pinned_by_policy)
  84. return false;
  85. // Allow to unpin a pinned app or pin a running app.
  86. return (item.type == TYPE_PINNED_APP && !target_pin) ||
  87. (item.type == TYPE_APP && target_pin);
  88. }
  89. void ShelfModel::UnpinAppWithID(const std::string& app_id) {
  90. // If the app is already not pinned, do nothing and return.
  91. if (!IsAppPinned(app_id))
  92. return;
  93. // Remove the item if it is closed, or mark it as unpinned.
  94. const int index = ItemIndexByID(ShelfID(app_id));
  95. ShelfItem item = items_[index];
  96. DCHECK_EQ(item.type, TYPE_PINNED_APP);
  97. DCHECK(!item.pinned_by_policy);
  98. if (item.status == STATUS_CLOSED) {
  99. RemoveItemAt(index);
  100. } else {
  101. item.type = TYPE_APP;
  102. Set(index, item);
  103. }
  104. }
  105. void ShelfModel::DestroyItemDelegates() {
  106. // Some ShelfItemDelegates access this model in their destructors and hence
  107. // need early cleanup.
  108. id_to_item_delegate_map_.clear();
  109. }
  110. int ShelfModel::Add(const ShelfItem& item,
  111. std::unique_ptr<ShelfItemDelegate> delegate) {
  112. return AddAt(items_.size(), item, std::move(delegate));
  113. }
  114. int ShelfModel::AddAt(int index,
  115. const ShelfItem& item,
  116. std::unique_ptr<ShelfItemDelegate> delegate) {
  117. // Update the delegate map immediately. We don't send a
  118. // ShelfItemDelegateChanged() call when adding items to the model.
  119. delegate->set_shelf_id(item.id);
  120. id_to_item_delegate_map_[item.id] = std::move(delegate);
  121. // Items should have unique non-empty ids to avoid undefined model behavior.
  122. DCHECK(!item.id.IsNull()) << " The id is null.";
  123. DCHECK_EQ(ItemIndexByID(item.id), -1) << " The id is not unique: " << item.id;
  124. index = ValidateInsertionIndex(item.type, index);
  125. items_.insert(items_.begin() + index, item);
  126. for (auto& observer : observers_)
  127. observer.ShelfItemAdded(index);
  128. return index;
  129. }
  130. void ShelfModel::RemoveItemAt(int index) {
  131. DCHECK(index >= 0 && index < item_count());
  132. ShelfItem old_item(items_[index]);
  133. items_.erase(items_.begin() + index);
  134. id_to_item_delegate_map_.erase(old_item.id);
  135. for (auto& observer : observers_)
  136. observer.ShelfItemRemoved(index, old_item);
  137. }
  138. std::unique_ptr<ShelfItemDelegate>
  139. ShelfModel::RemoveItemAndTakeShelfItemDelegate(const ShelfID& shelf_id) {
  140. const int index = ItemIndexByID(shelf_id);
  141. if (index < 0)
  142. return nullptr;
  143. auto it = id_to_item_delegate_map_.find(shelf_id);
  144. std::unique_ptr<ShelfItemDelegate> item = std::move(it->second);
  145. RemoveItemAt(index);
  146. return item;
  147. }
  148. bool ShelfModel::CanSwap(int index, bool with_next) const {
  149. const int target_index = with_next ? index + 1 : index - 1;
  150. // Out of bounds issues, or trying to swap the first item with the previous
  151. // one, or the last item with the next one.
  152. if (index < 0 || target_index >= item_count() || target_index < 0)
  153. return false;
  154. const ShelfItem source_item = items()[index];
  155. const ShelfItem target_item = items()[target_index];
  156. // Trying to swap two items of different pin states.
  157. if (!SamePinState(source_item.type, target_item.type))
  158. return false;
  159. return true;
  160. }
  161. bool ShelfModel::Swap(int index, bool with_next) {
  162. if (!CanSwap(index, with_next))
  163. return false;
  164. const int target_index = with_next ? index + 1 : index - 1;
  165. Move(index, target_index);
  166. return true;
  167. }
  168. void ShelfModel::Move(int index, int target_index) {
  169. if (index == target_index)
  170. return;
  171. // TODO: this needs to enforce valid ranges.
  172. ShelfItem item(items_[index]);
  173. items_.erase(items_.begin() + index);
  174. items_.insert(items_.begin() + target_index, item);
  175. for (auto& observer : observers_)
  176. observer.ShelfItemMoved(index, target_index);
  177. }
  178. void ShelfModel::Set(int index, const ShelfItem& item) {
  179. if (index < 0 || index >= item_count()) {
  180. NOTREACHED();
  181. return;
  182. }
  183. int new_index = item.type == items_[index].type
  184. ? index
  185. : ValidateInsertionIndex(item.type, index);
  186. ShelfItem old_item(items_[index]);
  187. items_[index] = item;
  188. DCHECK(old_item.id == item.id);
  189. for (auto& observer : observers_)
  190. observer.ShelfItemChanged(index, old_item);
  191. // If the type changes confirm that the item is still in the right order.
  192. if (new_index != index) {
  193. // The move function works by removing one item and then inserting it at the
  194. // new location. However - by removing the item first the order will change
  195. // so that our target index needs to be corrected.
  196. // TODO(skuhne): Moving this into the Move function breaks lots of unit
  197. // tests. So several functions were already using this incorrectly.
  198. // That needs to be cleaned up.
  199. if (index < new_index)
  200. new_index--;
  201. Move(index, new_index);
  202. }
  203. }
  204. void ShelfModel::UpdateItemsForDeskChange(
  205. const std::vector<ItemDeskUpdate>& items_desk_updates) {
  206. for (const auto& item : items_desk_updates) {
  207. const int index = item.index;
  208. DCHECK(index >= 0 && index < item_count());
  209. items_[index].is_on_active_desk = item.is_on_active_desk;
  210. }
  211. for (auto& observer : observers_)
  212. observer.ShelfItemsUpdatedForDeskChange();
  213. }
  214. // TODO(manucornet): Add some simple unit tests for this method.
  215. void ShelfModel::SetActiveShelfID(const ShelfID& shelf_id) {
  216. if (active_shelf_id_ == shelf_id)
  217. return;
  218. ShelfID old_active_id = active_shelf_id_;
  219. active_shelf_id_ = shelf_id;
  220. if (!old_active_id.IsNull())
  221. OnItemStatusChanged(old_active_id);
  222. if (!active_shelf_id_.IsNull())
  223. OnItemStatusChanged(active_shelf_id_);
  224. }
  225. void ShelfModel::OnItemStatusChanged(const ShelfID& id) {
  226. for (auto& observer : observers_)
  227. observer.ShelfItemStatusChanged(id);
  228. }
  229. void ShelfModel::OnItemRippedOff() {
  230. for (auto& observer : observers_)
  231. observer.ShelfItemRippedOff();
  232. }
  233. void ShelfModel::OnItemReturnedFromRipOff(int index) {
  234. for (auto& observer : observers_)
  235. observer.ShelfItemReturnedFromRipOff(index);
  236. }
  237. void ShelfModel::ToggleShelfParty() {
  238. in_shelf_party_ = !in_shelf_party_;
  239. for (auto& observer : observers_)
  240. observer.ShelfPartyToggled(in_shelf_party_);
  241. }
  242. int ShelfModel::ItemIndexByID(const ShelfID& shelf_id) const {
  243. for (size_t i = 0; i < items_.size(); ++i) {
  244. if (items_[i].id == shelf_id)
  245. return static_cast<int>(i);
  246. }
  247. return -1;
  248. }
  249. int ShelfModel::GetItemIndexForType(ShelfItemType type) {
  250. for (size_t i = 0; i < items_.size(); ++i) {
  251. if (items_[i].type == type)
  252. return i;
  253. }
  254. return -1;
  255. }
  256. const ShelfItem* ShelfModel::ItemByID(const ShelfID& shelf_id) const {
  257. int index = ItemIndexByID(shelf_id);
  258. return index >= 0 ? &items_[index] : nullptr;
  259. }
  260. int ShelfModel::ItemIndexByAppID(const std::string& app_id) const {
  261. for (size_t i = 0; i < items_.size(); ++i) {
  262. if (!app_id.compare(items_[i].id.app_id))
  263. return i;
  264. }
  265. return -1;
  266. }
  267. int ShelfModel::FirstRunningAppIndex() const {
  268. ShelfItem weight_dummy;
  269. weight_dummy.type = TYPE_APP;
  270. return std::lower_bound(items_.begin(), items_.end(), weight_dummy,
  271. CompareByWeight) -
  272. items_.begin();
  273. }
  274. void ShelfModel::ReplaceShelfItemDelegate(
  275. const ShelfID& shelf_id,
  276. std::unique_ptr<ShelfItemDelegate> item_delegate) {
  277. DCHECK(item_delegate);
  278. // Create a copy of the id that can be safely accessed if |shelf_id| is backed
  279. // by a controller that will be deleted in the assignment below.
  280. const ShelfID safe_shelf_id = shelf_id;
  281. item_delegate->set_shelf_id(safe_shelf_id);
  282. // This assignment replaces any ShelfItemDelegate already registered for
  283. // |shelf_id|.
  284. std::unique_ptr<ShelfItemDelegate> old_item_delegate =
  285. std::move(id_to_item_delegate_map_[safe_shelf_id]);
  286. id_to_item_delegate_map_[safe_shelf_id] = std::move(item_delegate);
  287. for (auto& observer : observers_) {
  288. observer.ShelfItemDelegateChanged(
  289. safe_shelf_id, old_item_delegate.get(),
  290. id_to_item_delegate_map_[safe_shelf_id].get());
  291. }
  292. }
  293. ShelfItemDelegate* ShelfModel::GetShelfItemDelegate(
  294. const ShelfID& shelf_id) const {
  295. auto it = id_to_item_delegate_map_.find(shelf_id);
  296. if (it != id_to_item_delegate_map_.end())
  297. return it->second.get();
  298. return nullptr;
  299. }
  300. void ShelfModel::SetShelfItemFactory(ShelfModel::ShelfItemFactory* factory) {
  301. shelf_item_factory_ = factory;
  302. }
  303. AppWindowShelfItemController* ShelfModel::GetAppWindowShelfItemController(
  304. const ShelfID& shelf_id) {
  305. ShelfItemDelegate* item_delegate = GetShelfItemDelegate(shelf_id);
  306. return item_delegate ? item_delegate->AsAppWindowShelfItemController()
  307. : nullptr;
  308. }
  309. void ShelfModel::AddObserver(ShelfModelObserver* observer) {
  310. observers_.AddObserver(observer);
  311. }
  312. void ShelfModel::RemoveObserver(ShelfModelObserver* observer) {
  313. observers_.RemoveObserver(observer);
  314. }
  315. int ShelfModel::ValidateInsertionIndex(ShelfItemType type, int index) const {
  316. DCHECK(index >= 0 && index <= item_count() + 1);
  317. // Clamp |index| to the allowed range for the type as determined by |weight|.
  318. ShelfItem weight_dummy;
  319. weight_dummy.type = type;
  320. index = std::max(std::lower_bound(items_.begin(), items_.end(), weight_dummy,
  321. CompareByWeight) -
  322. items_.begin(),
  323. static_cast<ShelfItems::difference_type>(index));
  324. index = std::min(std::upper_bound(items_.begin(), items_.end(), weight_dummy,
  325. CompareByWeight) -
  326. items_.begin(),
  327. static_cast<ShelfItems::difference_type>(index));
  328. return index;
  329. }
  330. void ShelfModel::UpdateItemNotification(const std::string& app_id,
  331. bool has_badge) {
  332. int index = ItemIndexByAppID(app_id);
  333. // If the item is not pinned or active on the shelf.
  334. if (index == -1)
  335. return;
  336. if (items_[index].has_notification == has_badge)
  337. return;
  338. items_[index].has_notification = has_badge;
  339. for (auto& observer : observers_)
  340. observer.ShelfItemChanged(index, items_[index]);
  341. }
  342. } // namespace ash