Files
XCEngine/new_editor/app/Hierarchy/ProductHierarchyModel.cpp

294 lines
8.4 KiB
C++
Raw Normal View History

#include "Hierarchy/ProductHierarchyModel.h"
#include <algorithm>
#include <functional>
#include <optional>
#include <sstream>
#include <utility>
namespace XCEngine::UI::Editor::App {
namespace {
const ProductHierarchyNode* FindNodeRecursive(
const std::vector<ProductHierarchyNode>& nodes,
std::string_view nodeId) {
for (const ProductHierarchyNode& node : nodes) {
if (node.nodeId == nodeId) {
return &node;
}
if (const ProductHierarchyNode* child =
FindNodeRecursive(node.children, nodeId);
child != nullptr) {
return child;
}
}
return nullptr;
}
ProductHierarchyNode* FindNodeRecursive(
std::vector<ProductHierarchyNode>& nodes,
std::string_view nodeId) {
for (ProductHierarchyNode& node : nodes) {
if (node.nodeId == nodeId) {
return &node;
}
if (ProductHierarchyNode* child =
FindNodeRecursive(node.children, nodeId);
child != nullptr) {
return child;
}
}
return nullptr;
}
bool FindNodeParentRecursive(
const std::vector<ProductHierarchyNode>& nodes,
std::string_view nodeId,
const ProductHierarchyNode*& parent) {
for (const ProductHierarchyNode& node : nodes) {
for (const ProductHierarchyNode& child : node.children) {
if (child.nodeId == nodeId) {
parent = &node;
return true;
}
}
if (FindNodeParentRecursive(node.children, nodeId, parent)) {
return true;
}
}
return false;
}
bool ExtractNodeRecursive(
std::vector<ProductHierarchyNode>& nodes,
std::string_view nodeId,
ProductHierarchyNode& extractedNode) {
for (std::size_t index = 0u; index < nodes.size(); ++index) {
if (nodes[index].nodeId == nodeId) {
extractedNode = std::move(nodes[index]);
nodes.erase(nodes.begin() + static_cast<std::ptrdiff_t>(index));
return true;
}
if (ExtractNodeRecursive(nodes[index].children, nodeId, extractedNode)) {
return true;
}
}
return false;
}
void BuildTreeItemsRecursive(
const std::vector<ProductHierarchyNode>& nodes,
std::uint32_t depth,
const ::XCEngine::UI::UITextureHandle& icon,
std::vector<Widgets::UIEditorTreeViewItem>& items) {
for (const ProductHierarchyNode& node : nodes) {
Widgets::UIEditorTreeViewItem item = {};
item.itemId = node.nodeId;
item.label = node.label;
item.depth = depth;
item.forceLeaf = node.children.empty();
item.leadingIcon = icon;
items.push_back(std::move(item));
BuildTreeItemsRecursive(node.children, depth + 1u, icon, items);
}
}
} // namespace
ProductHierarchyModel ProductHierarchyModel::BuildDefault() {
ProductHierarchyModel model = {};
model.m_roots = {
ProductHierarchyNode{ "main_camera", "Main Camera", {} },
ProductHierarchyNode{ "directional_light", "Directional Light", {} },
ProductHierarchyNode{
"player",
"Player",
{
ProductHierarchyNode{ "camera_pivot", "Camera Pivot", {} },
ProductHierarchyNode{ "player_mesh", "Mesh", {} }
} },
ProductHierarchyNode{
"environment",
"Environment",
{
ProductHierarchyNode{ "ground", "Ground", {} },
ProductHierarchyNode{
"props",
"Props",
{
ProductHierarchyNode{ "crate_01", "Crate_01", {} },
ProductHierarchyNode{ "barrel_01", "Barrel_01", {} }
} }
} }
};
model.m_nextGeneratedNodeId = 1u;
return model;
}
bool ProductHierarchyModel::Empty() const {
return m_roots.empty();
}
bool ProductHierarchyModel::ContainsNode(std::string_view nodeId) const {
return FindNode(nodeId) != nullptr;
}
const ProductHierarchyNode* ProductHierarchyModel::FindNode(std::string_view nodeId) const {
return FindNodeRecursive(m_roots, nodeId);
}
ProductHierarchyNode* ProductHierarchyModel::FindNode(std::string_view nodeId) {
return FindNodeRecursive(m_roots, nodeId);
}
std::optional<std::string> ProductHierarchyModel::GetParentId(std::string_view nodeId) const {
const ProductHierarchyNode* parent = nullptr;
if (!FindNodeParentRecursive(m_roots, nodeId, parent) || parent == nullptr) {
return std::nullopt;
}
return parent->nodeId;
}
bool ProductHierarchyModel::RenameNode(std::string_view nodeId, std::string label) {
ProductHierarchyNode* node = FindNode(nodeId);
if (node == nullptr || label.empty() || node->label == label) {
return false;
}
node->label = std::move(label);
return true;
}
std::string ProductHierarchyModel::CreateChild(
std::string_view parentId,
std::string_view label) {
ProductHierarchyNode* parent = FindNode(parentId);
if (parent == nullptr) {
return {};
}
ProductHierarchyNode node = {};
node.nodeId = AllocateNodeId();
node.label = label.empty() ? std::string("GameObject") : std::string(label);
parent->children.push_back(std::move(node));
return parent->children.back().nodeId;
}
bool ProductHierarchyModel::DeleteNode(std::string_view nodeId) {
if (nodeId.empty()) {
return false;
}
ProductHierarchyNode removed = {};
return ExtractNodeRecursive(m_roots, nodeId, removed);
}
bool ProductHierarchyModel::CanReparent(
std::string_view sourceNodeId,
std::string_view targetParentId) const {
if (sourceNodeId.empty()) {
return false;
}
const ProductHierarchyNode* source = FindNode(sourceNodeId);
if (source == nullptr) {
return false;
}
if (targetParentId.empty()) {
return true;
}
const ProductHierarchyNode* targetParent = FindNode(targetParentId);
return CanAdopt(sourceNodeId, targetParent);
}
bool ProductHierarchyModel::Reparent(
std::string_view sourceNodeId,
std::string_view targetParentId) {
if (!CanReparent(sourceNodeId, targetParentId) || targetParentId.empty()) {
return false;
}
const std::optional<std::string> currentParentId = GetParentId(sourceNodeId);
if (currentParentId.has_value() && currentParentId.value() == targetParentId) {
return false;
}
ProductHierarchyNode movedNode = {};
if (!ExtractNodeRecursive(m_roots, sourceNodeId, movedNode)) {
return false;
}
ProductHierarchyNode* targetParent = FindNode(targetParentId);
if (targetParent == nullptr) {
return false;
}
targetParent->children.push_back(std::move(movedNode));
return true;
}
bool ProductHierarchyModel::MoveToRoot(std::string_view sourceNodeId) {
if (sourceNodeId.empty() || !ContainsNode(sourceNodeId)) {
return false;
}
if (!GetParentId(sourceNodeId).has_value()) {
return false;
}
ProductHierarchyNode movedNode = {};
if (!ExtractNodeRecursive(m_roots, sourceNodeId, movedNode)) {
return false;
}
m_roots.push_back(std::move(movedNode));
return true;
}
std::vector<Widgets::UIEditorTreeViewItem> ProductHierarchyModel::BuildTreeItems(
const ::XCEngine::UI::UITextureHandle& icon) const {
std::vector<Widgets::UIEditorTreeViewItem> items = {};
BuildTreeItemsRecursive(m_roots, 0u, icon, items);
return items;
}
std::string ProductHierarchyModel::AllocateNodeId() {
std::ostringstream stream = {};
stream << "generated_node_" << m_nextGeneratedNodeId++;
return stream.str();
}
bool ProductHierarchyModel::CanAdopt(
std::string_view sourceNodeId,
const ProductHierarchyNode* targetParent) const {
if (targetParent == nullptr || sourceNodeId == targetParent->nodeId) {
return false;
}
const ProductHierarchyNode* source = FindNode(sourceNodeId);
if (source == nullptr) {
return false;
}
return !ContainsDescendant(*source, targetParent->nodeId);
}
bool ProductHierarchyModel::ContainsDescendant(
const ProductHierarchyNode& node,
std::string_view candidateId) const {
for (const ProductHierarchyNode& child : node.children) {
if (child.nodeId == candidateId || ContainsDescendant(child, candidateId)) {
return true;
}
}
return false;
}
} // namespace XCEngine::UI::Editor::App