blob: 3dd6e82e6d8bfc401976c8e464ac6a1680e75c23 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/tabs/vertical/tab_collection_node.h"
#include <vector>
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "chrome/browser/ui/tabs/tab_strip_api/utilities/tab_strip_api_utilities.h"
#include "chrome/browser/ui/views/tabs/vertical/vertical_pinned_tab_container_view.h"
#include "chrome/browser/ui/views/tabs/vertical/vertical_split_tab_view.h"
#include "chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h"
#include "chrome/browser/ui/views/tabs/vertical/vertical_tab_view.h"
#include "chrome/browser/ui/views/tabs/vertical/vertical_unpinned_tab_container_view.h"
#include "components/browser_apis/tab_strip/tab_strip_api_types.mojom.h"
#include "ui/views/view.h"
namespace {
TabCollectionNode::ViewFactory& GetViewFactory() {
static base::NoDestructor<TabCollectionNode::ViewFactory> factory;
return *factory;
}
class CollectionTestViewImpl : public views::View {
public:
explicit CollectionTestViewImpl(TabCollectionNode* node) {
node->set_add_child_to_node(
base::BindRepeating<TabCollectionNode::CustomAddChildView>(
&views::View::AddChildView, base::Unretained(this)));
}
~CollectionTestViewImpl() override = default;
};
} // anonymous namespace
base::CallbackListSubscription TabCollectionNode::RegisterWillDestroyCallback(
base::OnceClosure callback) {
return on_will_destroy_callback_list_.Add(std::move(callback));
}
// static
void TabCollectionNode::SetViewFactoryForTesting(ViewFactory factory) {
GetViewFactory() = std::move(factory);
}
// static
std::unique_ptr<views::View> TabCollectionNode::CreateViewForNode(
TabCollectionNode* node_for_view) {
if (GetViewFactory()) {
return GetViewFactory().Run(node_for_view);
}
switch (node_for_view->GetType()) {
case Type::kTabStrip:
return std::make_unique<VerticalTabStripView>(node_for_view);
case Type::kPinnedTabs:
return std::make_unique<VerticalPinnedTabContainerView>(node_for_view);
case Type::kUnpinnedTabs:
return std::make_unique<VerticalUnpinnedTabContainerView>(node_for_view);
case Type::kSplitTab:
return std::make_unique<VerticalSplitTabView>(node_for_view);
case Type::kTabGroup:
// TODO(crbug.com/442567916): support tab groups.
break;
case Type::kTab:
return std::make_unique<VerticalTabView>(node_for_view);
}
return std::make_unique<CollectionTestViewImpl>(node_for_view);
}
TabCollectionNode::TabCollectionNode(tabs_api::mojom::DataPtr data)
: data_(std::move(data)) {}
TabCollectionNode::~TabCollectionNode() {
on_will_destroy_callback_list_.Notify();
}
std::unique_ptr<views::View> TabCollectionNode::Initialize(
std::vector<tabs_api::mojom::ContainerPtr> child_containers) {
CHECK(children_.empty());
children_.reserve(child_containers.size());
std::unique_ptr<views::View> node_view = CreateAndSetView();
for (auto& child_container : child_containers) {
auto child_node =
std::make_unique<TabCollectionNode>(std::move(child_container->data));
auto child_node_view =
child_node->Initialize(std::move(child_container->children));
AddChild(std::move(child_node_view), std::move(child_node),
children_.size());
}
return node_view;
}
void TabCollectionNode::SetData(base::PassKey<TabCollectionNode> pass_key,
tabs_api::mojom::DataPtr data) {
data_ = std::move(data);
// TODO(crbug.com/439960283): Pipe data to node_view_.
}
// TODO(crbug.com/450976282): Consider having a map at the root level, or using
// path in the API, in order to not have to iterate through the whole collection
// node structure.
TabCollectionNode* TabCollectionNode::GetNodeForId(
const tabs_api::NodeId& node_id) {
if (tabs_api::utils::GetNodeId(*data_) == node_id) {
return this;
}
for (const auto& child : children_) {
if (TabCollectionNode* node = child->GetNodeForId(node_id)) {
return node;
}
}
return nullptr;
}
void TabCollectionNode::AddNewChild(base::PassKey<TabCollectionNode> pass_key,
tabs_api::mojom::DataPtr data,
size_t model_index) {
auto child_node = std::make_unique<TabCollectionNode>(std::move(data));
auto child_node_view = child_node->CreateAndSetView();
AddChild(std::move(child_node_view), std::move(child_node), model_index);
}
std::vector<views::View*> TabCollectionNode::GetDirectChildren() const {
std::vector<views::View*> child_views;
child_views.reserve(children_.size());
for (const auto& child : children_) {
child_views.push_back(child->node_view_);
}
return child_views;
}
std::unique_ptr<views::View> TabCollectionNode::CreateAndSetView() {
auto node_view = CreateViewForNode(this);
node_view_ = node_view.get();
return node_view;
}
void TabCollectionNode::AddChild(std::unique_ptr<views::View> child_node_view,
std::unique_ptr<TabCollectionNode> child_node,
size_t model_index) {
children_.insert(children_.begin() + model_index, std::move(child_node));
// Add child view after inserting the child node into children_, as adding the
// view may depend on the order of the node in children_.
if (add_child_to_node_) {
add_child_to_node_.Run(std::move(child_node_view));
} else {
node_view_->AddChildView(std::move(child_node_view));
}
}