168 lines
4.8 KiB
C++
168 lines
4.8 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <span>
|
|
#include <utility>
|
|
|
|
namespace XCEngine {
|
|
namespace UI {
|
|
namespace Widgets {
|
|
|
|
inline constexpr std::size_t kInvalidUIFlatHierarchyItemOffset = static_cast<std::size_t>(-1);
|
|
|
|
namespace Detail {
|
|
|
|
template <typename ResolveDepthFn>
|
|
float ResolveUIFlatHierarchyDepth(
|
|
std::span<const std::size_t> itemIndices,
|
|
std::size_t itemOffset,
|
|
ResolveDepthFn&& resolveDepth) {
|
|
if (itemOffset >= itemIndices.size()) {
|
|
return 0.0f;
|
|
}
|
|
|
|
const float depth = static_cast<float>(resolveDepth(itemIndices[itemOffset]));
|
|
return depth > 0.0f ? depth : 0.0f;
|
|
}
|
|
|
|
} // namespace Detail
|
|
|
|
template <typename ResolveDepthFn>
|
|
bool UIFlatHierarchyHasChildren(
|
|
std::span<const std::size_t> itemIndices,
|
|
std::size_t itemOffset,
|
|
ResolveDepthFn&& resolveDepth) {
|
|
if (itemOffset >= itemIndices.size()) {
|
|
return false;
|
|
}
|
|
|
|
const float itemDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
itemOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
for (std::size_t candidateOffset = itemOffset + 1u;
|
|
candidateOffset < itemIndices.size();
|
|
++candidateOffset) {
|
|
const float candidateDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
candidateOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
if (candidateDepth <= itemDepth) {
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename ResolveDepthFn>
|
|
std::size_t UIFlatHierarchyFindParentOffset(
|
|
std::span<const std::size_t> itemIndices,
|
|
std::size_t itemOffset,
|
|
ResolveDepthFn&& resolveDepth) {
|
|
if (itemOffset >= itemIndices.size()) {
|
|
return kInvalidUIFlatHierarchyItemOffset;
|
|
}
|
|
|
|
const float itemDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
itemOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
if (itemDepth <= 0.0f) {
|
|
return kInvalidUIFlatHierarchyItemOffset;
|
|
}
|
|
|
|
for (std::size_t candidateOffset = itemOffset; candidateOffset > 0u; --candidateOffset) {
|
|
const std::size_t previousOffset = candidateOffset - 1u;
|
|
const float previousDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
previousOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
if (previousDepth < itemDepth) {
|
|
return previousOffset;
|
|
}
|
|
}
|
|
|
|
return kInvalidUIFlatHierarchyItemOffset;
|
|
}
|
|
|
|
template <typename ResolveDepthFn, typename IsExpandedFn>
|
|
bool UIFlatHierarchyIsVisible(
|
|
std::span<const std::size_t> itemIndices,
|
|
std::size_t itemOffset,
|
|
ResolveDepthFn&& resolveDepth,
|
|
IsExpandedFn&& isExpanded) {
|
|
if (itemOffset >= itemIndices.size()) {
|
|
return false;
|
|
}
|
|
|
|
float requiredAncestorDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
itemOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
if (requiredAncestorDepth <= 0.0f) {
|
|
return true;
|
|
}
|
|
|
|
for (std::size_t candidateOffset = itemOffset;
|
|
candidateOffset > 0u && requiredAncestorDepth > 0.0f;
|
|
--candidateOffset) {
|
|
const std::size_t previousOffset = candidateOffset - 1u;
|
|
const float previousDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
previousOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
if (previousDepth < requiredAncestorDepth) {
|
|
if (!isExpanded(itemIndices[previousOffset])) {
|
|
return false;
|
|
}
|
|
|
|
requiredAncestorDepth = previousDepth;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename ResolveDepthFn, typename IsVisibleFn>
|
|
std::size_t UIFlatHierarchyFindFirstVisibleChildOffset(
|
|
std::span<const std::size_t> itemIndices,
|
|
std::size_t itemOffset,
|
|
ResolveDepthFn&& resolveDepth,
|
|
IsVisibleFn&& isVisible) {
|
|
if (!UIFlatHierarchyHasChildren(
|
|
itemIndices,
|
|
itemOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth))) {
|
|
return kInvalidUIFlatHierarchyItemOffset;
|
|
}
|
|
|
|
const float itemDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
itemOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
for (std::size_t candidateOffset = itemOffset + 1u;
|
|
candidateOffset < itemIndices.size();
|
|
++candidateOffset) {
|
|
const float candidateDepth = Detail::ResolveUIFlatHierarchyDepth(
|
|
itemIndices,
|
|
candidateOffset,
|
|
std::forward<ResolveDepthFn>(resolveDepth));
|
|
if (candidateDepth <= itemDepth) {
|
|
break;
|
|
}
|
|
|
|
if (isVisible(itemIndices[candidateOffset])) {
|
|
return candidateOffset;
|
|
}
|
|
}
|
|
|
|
return kInvalidUIFlatHierarchyItemOffset;
|
|
}
|
|
|
|
} // namespace Widgets
|
|
} // namespace UI
|
|
} // namespace XCEngine
|