| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COMPONENTS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_MODEL_H_ |
| #define COMPONENTS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_MODEL_H_ |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/observer_list.h" |
| #include "components/saved_tab_groups/saved_tab_group.h" |
| #include "components/saved_tab_groups/saved_tab_group_tab.h" |
| #include "components/sync/protocol/saved_tab_group_specifics.pb.h" |
| #include "components/tab_groups/tab_group_color.h" |
| #include "components/tab_groups/tab_group_id.h" |
| #include "components/tab_groups/tab_group_visual_data.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| class SavedTabGroupModelObserver; |
| class SavedTabGroup; |
| |
| // Serves to maintain the current state of all saved tab groups in the current |
| // session. |
| class SavedTabGroupModel { |
| public: |
| SavedTabGroupModel(); |
| SavedTabGroupModel(const SavedTabGroupModel&) = delete; |
| SavedTabGroupModel& operator=(const SavedTabGroupModel& other) = delete; |
| ~SavedTabGroupModel(); |
| |
| // Accessor for the underlying storage vector. |
| const std::vector<SavedTabGroup>& saved_tab_groups() const { |
| return saved_tab_groups_; |
| } |
| std::vector<SavedTabGroup> saved_tab_groups() { return saved_tab_groups_; } |
| |
| // Returns the index of the SavedTabGroup if it exists in the vector. Else |
| // absl::nullopt. |
| absl::optional<int> GetIndexOf( |
| const tab_groups::TabGroupId local_group_id) const; |
| absl::optional<int> GetIndexOf(const base::GUID& id) const; |
| |
| // Get a pointer to the SavedTabGroup from an ID. Returns nullptr if not in |
| // vector. |
| const SavedTabGroup* Get(const tab_groups::TabGroupId local_group_id) const; |
| const SavedTabGroup* Get(const base::GUID& id) const; |
| |
| // Methods for checking if a group is in the SavedTabGroupModel. |
| bool Contains(const tab_groups::TabGroupId& local_group_id) const { |
| return GetIndexOf(local_group_id).has_value(); |
| } |
| bool Contains(const base::GUID& id) const { |
| return GetIndexOf(id).has_value(); |
| } |
| |
| // Helper for getting number of SavedTabGroups in the vector. |
| int Count() const { return saved_tab_groups_.size(); } |
| |
| // Helper for getting empty state of the SavedTabGroup vector. |
| bool IsEmpty() const { return Count() <= 0; } |
| |
| // Add / Remove / Update a single tab group from the model. |
| void Add(SavedTabGroup saved_group); |
| void Remove(const tab_groups::TabGroupId local_group_id); |
| void Remove(const base::GUID& id); |
| void UpdateVisualData(const tab_groups::TabGroupId local_group_id, |
| const tab_groups::TabGroupVisualData* visual_data); |
| void UpdateVisualData(const base::GUID& id, |
| const tab_groups::TabGroupVisualData* visual_data); |
| |
| // Similar to the Add/Remove/Update but originate from sync. As such, these |
| // function do not notify sync observers of these changes to avoid looping |
| // calls. |
| void AddedFromSync(SavedTabGroup saved_group); |
| void RemovedFromSync(const tab_groups::TabGroupId local_group_id); |
| void RemovedFromSync(const base::GUID& id); |
| void UpdatedVisualDataFromSync( |
| const tab_groups::TabGroupId local_group_id, |
| const tab_groups::TabGroupVisualData* visual_data); |
| void UpdatedVisualDataFromSync( |
| const base::GUID& id, |
| const tab_groups::TabGroupVisualData* visual_data); |
| |
| SavedTabGroup* GetGroupContainingTab(const base::GUID& saved_tab_guid); |
| SavedTabGroup* GetGroupContainingTab(const base::Token& local_tab_id); |
| |
| // Adds a saved tab to `index` in the specified group denoted by `group_id` if |
| // it exists. If `update_tab_positions` is true, update the positions of all |
| // tabs in the group. |
| void AddTabToGroup(const base::GUID& group_id, |
| SavedTabGroupTab tab, |
| bool update_tab_positions = false); |
| |
| // Calls the UpdateTab method on a group found by group id in the model. |
| // Calls the observer function SavedTabGroupUpdatedLocally. |
| void UpdateTabInGroup(const base::GUID& group_id, SavedTabGroupTab tab); |
| |
| // Removes saved tab `tab_id` in the specified group denoted by |
| // `group_id` if it exists. We delete the group instead if the last tab is |
| // removed from it. If `update_tab_positions` is true, update the positions of |
| // all tabs in the group and notify sync of the changes. |
| void RemoveTabFromGroup(const base::GUID& group_id, |
| const base::GUID& tab_id, |
| bool update_tab_positions = false); |
| |
| // Replaces a saved tab `tab_id` in the specified group denoted by |
| // `group_id` if it exists with `new_tab`. |
| void ReplaceTabInGroupAt(const base::GUID& group_id, |
| const base::GUID& tab_id, |
| SavedTabGroupTab new_tab); |
| |
| // Moves a saved tab from its current position to `index` in the specified |
| // group denoted by `group_id` if it exists. |
| void MoveTabInGroupTo(const base::GUID& group_id, |
| const base::GUID& tab_id, |
| int index); |
| |
| // Attempts to merge the sync_specific with the local object that holds the |
| // same guid. |
| std::unique_ptr<sync_pb::SavedTabGroupSpecifics> MergeGroup( |
| const sync_pb::SavedTabGroupSpecifics& sync_specific); |
| std::unique_ptr<sync_pb::SavedTabGroupSpecifics> MergeTab( |
| const sync_pb::SavedTabGroupSpecifics& sync_specific); |
| |
| // Changes the index of a given tab group by id. The new index provided is the |
| // expected index after the group is removed. |
| void Reorder(const base::GUID& id, int new_index); |
| |
| // Loads the entries (a sync_pb::SavedTabGroupSpecifics can be a group or a |
| // tab) saved locally in the model type store (local storage) and attempts to |
| // reconstruct the model by matching groups with their tabs using their |
| // `group_id`'s. Note: Any tabs that do not have a matching group, will be |
| // returned to the bridge to keep track of. |
| std::vector<sync_pb::SavedTabGroupSpecifics> LoadStoredEntries( |
| std::vector<sync_pb::SavedTabGroupSpecifics> entries); |
| |
| // Functions that should be called when a SavedTabGroup's corresponding |
| // TabGroup is closed or opened. |
| void OnGroupOpenedInTabStrip(const base::GUID& id, |
| const tab_groups::TabGroupId& local_group_id); |
| void OnGroupClosedInTabStrip(const tab_groups::TabGroupId& local_group_id); |
| |
| // Add/Remove observers for this model. |
| void AddObserver(SavedTabGroupModelObserver* observer); |
| void RemoveObserver(SavedTabGroupModelObserver* observer); |
| |
| private: |
| // Updates all group positions to match the index they are currently stored |
| // at. |
| void UpdateGroupPositionsImpl(); |
| |
| // Insert `group` into sorted order based on its position compared to already |
| // stored groups in `saved_tab_groups_`. It should be noted that |
| // `saved_tab_groups` must already be in sorted order for this function to |
| // work as intended. To do this, UpdatePositionsImpl() can be called. |
| void InsertGroupImpl(const SavedTabGroup& group); |
| |
| // Implementations of CRUD operations. |
| std::unique_ptr<SavedTabGroup> RemoveImpl(int index); |
| void UpdateVisualDataImpl(int index, |
| const tab_groups::TabGroupVisualData* visual_data); |
| |
| // Obsevers of the model. |
| base::ObserverList<SavedTabGroupModelObserver>::Unchecked observers_; |
| |
| // Storage of all saved tab groups in the order they are displayed. The |
| // position of the groups must maintain sorted order as sync may not propagate |
| // an entire update completely leaving us with missing groups / gaps between |
| // the positions. |
| std::vector<SavedTabGroup> saved_tab_groups_; |
| }; |
| |
| #endif // COMPONENTS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_MODEL_H_ |