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_