Unlink XCNewEditor default shell from ImGui compat slice

This commit is contained in:
2026-04-05 16:32:43 +08:00
parent 712cc79c44
commit 74b1280aa6
12 changed files with 1128 additions and 57 deletions

View File

@@ -5,7 +5,9 @@
set(UI_TEST_SOURCES
test_ui_core.cpp
test_ui_editor_collection_primitives.cpp
test_ui_editor_panel_chrome.cpp
test_ui_expansion_model.cpp
test_ui_flat_hierarchy_helpers.cpp
test_ui_keyboard_navigation_model.cpp
test_ui_property_edit_model.cpp
test_layout_engine.cpp

View File

@@ -0,0 +1,126 @@
#include <gtest/gtest.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIEditorPanelChrome.h>
namespace {
using XCEngine::UI::UIColor;
using XCEngine::UI::UIDrawCommandType;
using XCEngine::UI::UIDrawList;
using XCEngine::UI::UIRect;
using XCEngine::UI::Widgets::AppendUIEditorPanelChromeBackground;
using XCEngine::UI::Widgets::AppendUIEditorPanelChromeForeground;
using XCEngine::UI::Widgets::BuildUIEditorPanelChromeHeaderRect;
using XCEngine::UI::Widgets::ResolveUIEditorPanelChromeBorderColor;
using XCEngine::UI::Widgets::ResolveUIEditorPanelChromeBorderThickness;
using XCEngine::UI::Widgets::UIEditorPanelChromePalette;
using XCEngine::UI::Widgets::UIEditorPanelChromeState;
using XCEngine::UI::Widgets::UIEditorPanelChromeText;
void ExpectColorEq(
const UIColor& actual,
const UIColor& expected) {
EXPECT_FLOAT_EQ(actual.r, expected.r);
EXPECT_FLOAT_EQ(actual.g, expected.g);
EXPECT_FLOAT_EQ(actual.b, expected.b);
EXPECT_FLOAT_EQ(actual.a, expected.a);
}
TEST(UIEditorPanelChromeTest, HeaderRectAndBorderPolicyMatchNativeShellCardChrome) {
const UIRect panelRect(100.0f, 200.0f, 320.0f, 180.0f);
const UIEditorPanelChromePalette palette = {};
const auto headerRect = BuildUIEditorPanelChromeHeaderRect(panelRect);
EXPECT_FLOAT_EQ(headerRect.x, 100.0f);
EXPECT_FLOAT_EQ(headerRect.y, 200.0f);
EXPECT_FLOAT_EQ(headerRect.width, 320.0f);
EXPECT_FLOAT_EQ(headerRect.height, 42.0f);
ExpectColorEq(
ResolveUIEditorPanelChromeBorderColor(UIEditorPanelChromeState(), palette),
palette.borderColor);
ExpectColorEq(
ResolveUIEditorPanelChromeBorderColor(UIEditorPanelChromeState{ false, true }, palette),
palette.hoveredAccentColor);
ExpectColorEq(
ResolveUIEditorPanelChromeBorderColor(UIEditorPanelChromeState{ true, true }, palette),
palette.accentColor);
EXPECT_FLOAT_EQ(ResolveUIEditorPanelChromeBorderThickness(UIEditorPanelChromeState()), 1.0f);
EXPECT_FLOAT_EQ(ResolveUIEditorPanelChromeBorderThickness(UIEditorPanelChromeState{ true, false }), 2.0f);
}
TEST(UIEditorPanelChromeTest, BackgroundAppendEmitsSurfaceOutlineAndHeaderFill) {
UIDrawList drawList("PanelChrome");
const UIRect panelRect(40.0f, 60.0f, 400.0f, 280.0f);
const UIEditorPanelChromeState state{ true, false };
const UIEditorPanelChromePalette palette = {};
AppendUIEditorPanelChromeBackground(drawList, panelRect, state, palette);
ASSERT_EQ(drawList.GetCommandCount(), 3u);
const auto& commands = drawList.GetCommands();
EXPECT_EQ(commands[0].type, UIDrawCommandType::FilledRect);
EXPECT_EQ(commands[1].type, UIDrawCommandType::RectOutline);
EXPECT_EQ(commands[2].type, UIDrawCommandType::FilledRect);
EXPECT_FLOAT_EQ(commands[0].rect.x, 40.0f);
EXPECT_FLOAT_EQ(commands[0].rounding, 18.0f);
ExpectColorEq(commands[0].color, palette.surfaceColor);
EXPECT_FLOAT_EQ(commands[1].thickness, 2.0f);
EXPECT_FLOAT_EQ(commands[1].rounding, 18.0f);
ExpectColorEq(commands[1].color, palette.accentColor);
EXPECT_FLOAT_EQ(commands[2].rect.height, 42.0f);
EXPECT_FLOAT_EQ(commands[2].rounding, 18.0f);
ExpectColorEq(commands[2].color, palette.headerColor);
}
TEST(UIEditorPanelChromeTest, ForegroundAppendPlacesTitleSubtitleAndFooterAtCurrentOffsets) {
UIDrawList drawList("PanelChromeText");
const UIRect panelRect(100.0f, 200.0f, 320.0f, 180.0f);
const UIEditorPanelChromePalette palette = {};
const UIEditorPanelChromeText text{
"XCUI Demo",
"native queued offscreen surface",
"Active | 42 elements | 9 cmds"
};
AppendUIEditorPanelChromeForeground(drawList, panelRect, text, palette);
ASSERT_EQ(drawList.GetCommandCount(), 3u);
const auto& commands = drawList.GetCommands();
EXPECT_EQ(commands[0].type, UIDrawCommandType::Text);
EXPECT_EQ(commands[1].type, UIDrawCommandType::Text);
EXPECT_EQ(commands[2].type, UIDrawCommandType::Text);
EXPECT_EQ(commands[0].text, "XCUI Demo");
EXPECT_FLOAT_EQ(commands[0].position.x, 116.0f);
EXPECT_FLOAT_EQ(commands[0].position.y, 212.0f);
ExpectColorEq(commands[0].color, palette.textPrimary);
EXPECT_EQ(commands[1].text, "native queued offscreen surface");
EXPECT_FLOAT_EQ(commands[1].position.x, 116.0f);
EXPECT_FLOAT_EQ(commands[1].position.y, 228.0f);
ExpectColorEq(commands[1].color, palette.textSecondary);
EXPECT_EQ(commands[2].text, "Active | 42 elements | 9 cmds");
EXPECT_FLOAT_EQ(commands[2].position.x, 116.0f);
EXPECT_FLOAT_EQ(commands[2].position.y, 362.0f);
ExpectColorEq(commands[2].color, palette.textMuted);
}
TEST(UIEditorPanelChromeTest, ForegroundAppendSkipsEmptyStrings) {
UIDrawList drawList("PanelChromeEmptyText");
AppendUIEditorPanelChromeForeground(
drawList,
UIRect(0.0f, 0.0f, 320.0f, 180.0f),
UIEditorPanelChromeText{});
EXPECT_EQ(drawList.GetCommandCount(), 0u);
}
} // namespace

View File

@@ -0,0 +1,145 @@
#include <gtest/gtest.h>
#include <XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h>
#include <array>
#include <unordered_set>
#include <vector>
namespace {
using XCEngine::UI::Widgets::kInvalidUIFlatHierarchyItemOffset;
using XCEngine::UI::Widgets::UIFlatHierarchyFindFirstVisibleChildOffset;
using XCEngine::UI::Widgets::UIFlatHierarchyFindParentOffset;
using XCEngine::UI::Widgets::UIFlatHierarchyHasChildren;
using XCEngine::UI::Widgets::UIFlatHierarchyIsVisible;
struct FlatHierarchyItem {
float depth = 0.0f;
};
TEST(UIFlatHierarchyHelpersTest, HasChildrenAndParentResolutionTrackIndentedBranches) {
const std::array<FlatHierarchyItem, 5> items = {{
{ 0.0f },
{ 1.0f },
{ 2.0f },
{ 1.0f },
{ 0.0f },
}};
const std::vector<std::size_t> itemIndices = { 0u, 1u, 2u, 3u, 4u };
const auto resolveDepth = [&items](std::size_t itemIndex) {
return items[itemIndex].depth;
};
EXPECT_TRUE(UIFlatHierarchyHasChildren(itemIndices, 0u, resolveDepth));
EXPECT_TRUE(UIFlatHierarchyHasChildren(itemIndices, 1u, resolveDepth));
EXPECT_FALSE(UIFlatHierarchyHasChildren(itemIndices, 2u, resolveDepth));
EXPECT_FALSE(UIFlatHierarchyHasChildren(itemIndices, 3u, resolveDepth));
EXPECT_FALSE(UIFlatHierarchyHasChildren(itemIndices, 4u, resolveDepth));
EXPECT_EQ(
UIFlatHierarchyFindParentOffset(itemIndices, 0u, resolveDepth),
kInvalidUIFlatHierarchyItemOffset);
EXPECT_EQ(UIFlatHierarchyFindParentOffset(itemIndices, 1u, resolveDepth), 0u);
EXPECT_EQ(UIFlatHierarchyFindParentOffset(itemIndices, 2u, resolveDepth), 1u);
EXPECT_EQ(UIFlatHierarchyFindParentOffset(itemIndices, 3u, resolveDepth), 0u);
EXPECT_EQ(
UIFlatHierarchyFindParentOffset(itemIndices, 99u, resolveDepth),
kInvalidUIFlatHierarchyItemOffset);
}
TEST(UIFlatHierarchyHelpersTest, VisibilityFollowsCollapsedAncestorStateAcrossDepthTransitions) {
const std::array<FlatHierarchyItem, 4> items = {{
{ 0.0f },
{ 1.0f },
{ 2.0f },
{ 1.0f },
}};
const std::vector<std::size_t> itemIndices = { 0u, 1u, 2u, 3u };
const auto resolveDepth = [&items](std::size_t itemIndex) {
return items[itemIndex].depth;
};
std::unordered_set<std::size_t> expandedItems = { 0u };
const auto isExpanded = [&expandedItems](std::size_t itemIndex) {
return expandedItems.contains(itemIndex);
};
EXPECT_TRUE(UIFlatHierarchyIsVisible(itemIndices, 0u, resolveDepth, isExpanded));
EXPECT_TRUE(UIFlatHierarchyIsVisible(itemIndices, 1u, resolveDepth, isExpanded));
EXPECT_FALSE(UIFlatHierarchyIsVisible(itemIndices, 2u, resolveDepth, isExpanded));
EXPECT_TRUE(UIFlatHierarchyIsVisible(itemIndices, 3u, resolveDepth, isExpanded));
expandedItems.insert(1u);
EXPECT_TRUE(UIFlatHierarchyIsVisible(itemIndices, 2u, resolveDepth, isExpanded));
expandedItems.clear();
EXPECT_FALSE(UIFlatHierarchyIsVisible(itemIndices, 1u, resolveDepth, isExpanded));
EXPECT_FALSE(UIFlatHierarchyIsVisible(itemIndices, 2u, resolveDepth, isExpanded));
}
TEST(UIFlatHierarchyHelpersTest, FirstVisibleChildSkipsCollapsedDescendantsUntilExpanded) {
const std::array<FlatHierarchyItem, 4> items = {{
{ 0.0f },
{ 1.0f },
{ 2.0f },
{ 1.0f },
}};
const std::vector<std::size_t> itemIndices = { 0u, 1u, 2u, 3u };
const auto resolveDepth = [&items](std::size_t itemIndex) {
return items[itemIndex].depth;
};
std::unordered_set<std::size_t> expandedItems = { 0u };
const auto isExpanded = [&expandedItems](std::size_t itemIndex) {
return expandedItems.contains(itemIndex);
};
const auto isVisible = [&itemIndices, &resolveDepth, &isExpanded](std::size_t itemIndex) {
for (std::size_t itemOffset = 0; itemOffset < itemIndices.size(); ++itemOffset) {
if (itemIndices[itemOffset] == itemIndex) {
return UIFlatHierarchyIsVisible(itemIndices, itemOffset, resolveDepth, isExpanded);
}
}
return false;
};
EXPECT_EQ(
UIFlatHierarchyFindFirstVisibleChildOffset(itemIndices, 0u, resolveDepth, isVisible),
1u);
EXPECT_EQ(
UIFlatHierarchyFindFirstVisibleChildOffset(itemIndices, 1u, resolveDepth, isVisible),
kInvalidUIFlatHierarchyItemOffset);
expandedItems.insert(1u);
EXPECT_EQ(
UIFlatHierarchyFindFirstVisibleChildOffset(itemIndices, 1u, resolveDepth, isVisible),
2u);
}
TEST(UIFlatHierarchyHelpersTest, NegativeDepthsClampToRootsForHierarchyQueries) {
const std::array<FlatHierarchyItem, 3> items = {{
{ -3.0f },
{ 1.0f },
{ -1.0f },
}};
const std::vector<std::size_t> itemIndices = { 0u, 1u, 2u };
const auto resolveDepth = [&items](std::size_t itemIndex) {
return items[itemIndex].depth;
};
const auto isExpanded = [](std::size_t itemIndex) {
return itemIndex == 0u;
};
EXPECT_TRUE(UIFlatHierarchyHasChildren(itemIndices, 0u, resolveDepth));
EXPECT_EQ(UIFlatHierarchyFindParentOffset(itemIndices, 1u, resolveDepth), 0u);
EXPECT_EQ(
UIFlatHierarchyFindParentOffset(itemIndices, 2u, resolveDepth),
kInvalidUIFlatHierarchyItemOffset);
EXPECT_TRUE(UIFlatHierarchyIsVisible(itemIndices, 2u, resolveDepth, isExpanded));
}
} // namespace