// Copyright (c) 2025 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. #include "brave/components/tabs/public/tree_tab_node.h" #include #include "base/functional/callback_helpers.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "brave/components/tabs/public/tree_tab_node_id.h" #include "brave/components/tabs/public/tree_tab_node_tab_collection.h" #include "components/tabs/public/split_tab_data.h" #include "components/tabs/public/tab_interface.h" namespace tabs { const TreeTabNode& TreeTabNode::GetEmptyTreeTabNode() { static base::NoDestructor empty_tree_tab_node_tab_collection( tree_tab::TreeTabNodeId::GenerateNew(), base::WrapUnique(nullptr), base::DoNothing(), base::DoNothing()); return empty_tree_tab_node_tab_collection->node(); } TreeTabNode::TreeTabNode(TreeTabNodeTabCollection& collection, const tree_tab::TreeTabNodeId& id) : collection_(collection), id_(id) {} int TreeTabNode::GetTreeHeight() const { return collection_->GetTopLevelAncestor()->node().height(); } std::vector TreeTabNode::GetTabs() const { const auto& value = collection_->current_value(); if (!value.has_value()) { return {}; } if (const base::WeakPtr* tab_ptr = std::get_if>(&*value)) { if (const TabInterface* tab = tab_ptr->get()) { return {tab}; } return {}; } if (const raw_ptr* split_ptr = std::get_if>(&*value)) { SplitTabCollection* split = split_ptr->get(); if (split && split->data()) { std::vector result; for (TabInterface* tab : split->data()->ListTabs()) { result.push_back(tab); } return result; } return {}; } if (const raw_ptr* group_ptr = std::get_if>(&*value)) { TabGroupTabCollection* group = group_ptr->get(); if (group) { std::vector result; for (TabInterface* tab : group->GetTabsRecursive()) { result.push_back(tab); } return result; } return {}; } return {}; } std::optional TreeTabNode::GetClosestCollapsedAncestorId() const { const TabCollection* current = collection_->GetParentCollection(); while (current && current->type() == TabCollection::Type::TREE_NODE) { const auto* parent_tree = static_cast(current); if (parent_tree->node().collapsed()) { return parent_tree->node().id(); } current = current->GetParentCollection(); } return std::nullopt; } void TreeTabNode::CollectDescendantIds( std::vector& out) { for (const auto& child : collection_->GetTreeNodeChildren()) { if (std::holds_alternative(child)) { TabCollection* collection = std::get(child); if (collection->type() != TabCollection::Type::TREE_NODE) { continue; } auto* child_tree = static_cast(collection); const tree_tab::TreeTabNodeId& child_id = child_tree->node().id(); out.push_back(child_id); child_tree->node().CollectDescendantIds(out); } } } void TreeTabNode::CollectUncollapsedDescendantIds( std::vector& out) { for (const auto& child : collection_->GetTreeNodeChildren()) { if (std::holds_alternative(child)) { TabCollection* collection = std::get(child); if (collection->type() != TabCollection::Type::TREE_NODE) { continue; } auto* child_tree = static_cast(collection); TreeTabNode& child_node = child_tree->node(); out.push_back(child_node.id()); if (!child_node.collapsed()) { child_node.CollectUncollapsedDescendantIds(out); } } } } std::optional TreeTabNode::GetParentTreeNodeId() const { const TabCollection* parent = collection_->GetParentCollection(); while (parent) { if (parent->type() == TabCollection::Type::TREE_NODE) { return static_cast(parent)->node().id(); } if (parent->type() == TabCollection::Type::UNPINNED || parent->type() == TabCollection::Type::PINNED || parent->type() == TabCollection::Type::TABSTRIP) { break; } parent = parent->GetParentCollection(); } return std::nullopt; } int TreeTabNode::CalculateLevelAndHeightRecursively( base::PassKey pass_key) { return CalculateLevelAndHeightRecursivelyImpl(); } void TreeTabNode::OnChildHeightChanged( base::PassKey pass_key) { OnChildHeightChangedImpl(); } int TreeTabNode::CalculateLevelAndHeightRecursivelyImpl() { auto* parent_collection = collection_->GetParentCollection(); if (!parent_collection || parent_collection->type() != TabCollection::Type::TREE_NODE) { // If there's no parent or the parent is not a tree node, this is the root. level_ = 0; } else { auto* parent_tree_node = static_cast(parent_collection); level_ = parent_tree_node->node().level_ + 1; } int max_height = std::numeric_limits::min(); for (const auto& child : collection_->GetTreeNodeChildren()) { if (std::holds_alternative(child)) { auto* collection = std::get(child); if (collection->type() != TabCollection::Type::TREE_NODE) { // If non-tree node child, e.g. split or group, height would be 0 for // this node as it should not have descendants. continue; } max_height = std::max(max_height, static_cast(collection) ->node() .CalculateLevelAndHeightRecursivelyImpl() + 1); } } height_ = (max_height == std::numeric_limits::min()) ? 0 : max_height; return height_; } void TreeTabNode::OnChildHeightChangedImpl() { // Update height of this node. int max_height = std::numeric_limits::min(); for (const auto& child : collection_->GetTreeNodeChildren()) { if (std::holds_alternative(child)) { auto* collection = std::get(child); if (collection->type() != TabCollection::Type::TREE_NODE) { // If non-tree node child, height would be 1 for this node max_height = std::max(max_height, 1); continue; } max_height = std::max( max_height, static_cast(collection)->node().height_ + 1); } } auto new_height = (max_height == std::numeric_limits::min()) ? 0 : max_height; if (new_height == height_) { return; } height_ = new_height; if (auto* parent_collection = collection_->GetParentCollection(); parent_collection && parent_collection->type() == TabCollection::Type::TREE_NODE) { static_cast(parent_collection) ->node() .OnChildHeightChangedImpl(); } } } // namespace tabs