Add XCUI ImGui transition backend MVP
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Subplan 05:XCUI ImGui Transition Backend
|
||||
|
||||
目标:
|
||||
|
||||
- 在过渡阶段,用 ImGui 只做宿主窗口与 draw submission 容器。
|
||||
- 让 XCUI 自己生成 draw list,再由 ImGui backend 负责落屏。
|
||||
|
||||
负责人边界:
|
||||
|
||||
- 负责 `editor/src/XCUIBackend/` 或等价新目录。
|
||||
- 负责 XCUI draw primitive 到 ImGui draw call 的映射。
|
||||
- 不负责 XCUI tree、布局、样式的内部规则。
|
||||
|
||||
建议目录:
|
||||
|
||||
- `editor/src/XCUIBackend/`
|
||||
- `editor/src/UI/` 中与 XCUI backend 直接相关的桥接代码
|
||||
- `tests/Editor` 中 backend 相关测试
|
||||
|
||||
前置依赖:
|
||||
|
||||
- 需要 `Subplan 01` 给出稳定 draw data / frame submission 契约。
|
||||
- 需要 `Subplan 03` 提供样式查询结果。
|
||||
|
||||
现在就可以先做的内容:
|
||||
|
||||
- 定义 `UIDrawList` / `UIDrawCommand` / `UIDrawText` / `UIDrawImage` / `UIDrawClip`
|
||||
- 先做矩形、边框、文字、图片四类 primitive
|
||||
- 设计 frame begin / submit / end 的 adapter 流程
|
||||
- 写一个最小 demo panel,用 XCUI draw list 通过 ImGui 显示
|
||||
|
||||
明确不做:
|
||||
|
||||
- 不做 RHI native backend
|
||||
- 不做 docking 逻辑
|
||||
|
||||
交付物:
|
||||
|
||||
- XCUI -> ImGui 过渡 backend
|
||||
- primitive 转换测试或快照测试
|
||||
- 最小 editor 接入样例
|
||||
|
||||
验收标准:
|
||||
|
||||
- XCUI 逻辑层不直接依赖 ImGui API
|
||||
- ImGui 只出现在 backend 适配层
|
||||
- 可以渲染基础控件和文本
|
||||
@@ -100,6 +100,7 @@ add_executable(${PROJECT_NAME} WIN32
|
||||
src/panels/InspectorPanel.cpp
|
||||
src/panels/ConsolePanel.cpp
|
||||
src/panels/ProjectPanel.cpp
|
||||
src/panels/XCUIDemoPanel.cpp
|
||||
src/UI/BuiltInIcons.cpp
|
||||
src/Layers/EditorLayer.cpp
|
||||
${IMGUI_SOURCES}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "panels/PanelCollection.h"
|
||||
#include "panels/ProjectPanel.h"
|
||||
#include "panels/SceneViewPanel.h"
|
||||
#include "panels/XCUIDemoPanel.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
|
||||
@@ -34,6 +35,7 @@ public:
|
||||
m_panels.Emplace<InspectorPanel>();
|
||||
m_panels.Emplace<ConsolePanel>();
|
||||
m_projectPanel = &m_panels.Emplace<ProjectPanel>();
|
||||
m_panels.Emplace<XCUIDemoPanel>();
|
||||
m_dockLayoutController = std::make_unique<DockLayoutController>();
|
||||
|
||||
m_projectPanel->Initialize(context.GetProjectPath());
|
||||
|
||||
188
editor/src/XCUIBackend/ImGuiTransitionBackend.h
Normal file
188
editor/src/XCUIBackend/ImGuiTransitionBackend.h
Normal file
@@ -0,0 +1,188 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace XCUIBackend {
|
||||
|
||||
class ImGuiTransitionBackend {
|
||||
public:
|
||||
void BeginFrame() {
|
||||
m_lastFlushedDrawListCount = 0;
|
||||
m_lastFlushedCommandCount = 0;
|
||||
m_pendingCommandCount = 0;
|
||||
m_pendingDrawLists.clear();
|
||||
}
|
||||
|
||||
void Submit(const ::XCEngine::UI::UIDrawList& drawList) {
|
||||
m_pendingCommandCount += drawList.GetCommandCount();
|
||||
m_pendingDrawLists.push_back(drawList);
|
||||
}
|
||||
|
||||
void Submit(::XCEngine::UI::UIDrawList&& drawList) {
|
||||
m_pendingCommandCount += drawList.GetCommandCount();
|
||||
m_pendingDrawLists.push_back(std::move(drawList));
|
||||
}
|
||||
|
||||
void Submit(const ::XCEngine::UI::UIDrawData& drawData) {
|
||||
for (const ::XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) {
|
||||
Submit(drawList);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasPendingDrawData() const {
|
||||
return !m_pendingDrawLists.empty();
|
||||
}
|
||||
|
||||
std::size_t GetPendingDrawListCount() const {
|
||||
return m_pendingDrawLists.size();
|
||||
}
|
||||
|
||||
std::size_t GetPendingCommandCount() const {
|
||||
return m_pendingCommandCount;
|
||||
}
|
||||
|
||||
std::size_t GetLastFlushedDrawListCount() const {
|
||||
return m_lastFlushedDrawListCount;
|
||||
}
|
||||
|
||||
std::size_t GetLastFlushedCommandCount() const {
|
||||
return m_lastFlushedCommandCount;
|
||||
}
|
||||
|
||||
bool EndFrame(ImDrawList* targetDrawList = nullptr) {
|
||||
ImDrawList* drawList = targetDrawList != nullptr ? targetDrawList : ImGui::GetWindowDrawList();
|
||||
if (drawList == nullptr) {
|
||||
ClearPendingState();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t clipDepth = 0;
|
||||
for (const ::XCEngine::UI::UIDrawList& pendingDrawList : m_pendingDrawLists) {
|
||||
for (const ::XCEngine::UI::UIDrawCommand& command : pendingDrawList.GetCommands()) {
|
||||
RenderCommand(*drawList, command, clipDepth);
|
||||
}
|
||||
}
|
||||
|
||||
while (clipDepth > 0) {
|
||||
drawList->PopClipRect();
|
||||
--clipDepth;
|
||||
}
|
||||
|
||||
m_lastFlushedDrawListCount = m_pendingDrawLists.size();
|
||||
m_lastFlushedCommandCount = m_pendingCommandCount;
|
||||
ClearPendingState();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static ImVec2 ToImVec2(const ::XCEngine::UI::UIPoint& point) {
|
||||
return ImVec2(point.x, point.y);
|
||||
}
|
||||
|
||||
static ImVec2 ToImVec2Min(const ::XCEngine::UI::UIRect& rect) {
|
||||
return ImVec2(rect.x, rect.y);
|
||||
}
|
||||
|
||||
static ImVec2 ToImVec2Max(const ::XCEngine::UI::UIRect& rect) {
|
||||
return ImVec2(rect.x + rect.width, rect.y + rect.height);
|
||||
}
|
||||
|
||||
static ImTextureID ToImTextureId(const ::XCEngine::UI::UITextureHandle& texture) {
|
||||
return static_cast<ImTextureID>(texture.nativeHandle);
|
||||
}
|
||||
|
||||
static ImU32 ToImU32(const ::XCEngine::UI::UIColor& color) {
|
||||
const float r = (std::max)(0.0f, (std::min)(1.0f, color.r));
|
||||
const float g = (std::max)(0.0f, (std::min)(1.0f, color.g));
|
||||
const float b = (std::max)(0.0f, (std::min)(1.0f, color.b));
|
||||
const float a = (std::max)(0.0f, (std::min)(1.0f, color.a));
|
||||
return IM_COL32(
|
||||
static_cast<int>(r * 255.0f),
|
||||
static_cast<int>(g * 255.0f),
|
||||
static_cast<int>(b * 255.0f),
|
||||
static_cast<int>(a * 255.0f));
|
||||
}
|
||||
|
||||
static void RenderCommand(
|
||||
ImDrawList& drawList,
|
||||
const ::XCEngine::UI::UIDrawCommand& command,
|
||||
std::size_t& clipDepth) {
|
||||
switch (command.type) {
|
||||
case ::XCEngine::UI::UIDrawCommandType::FilledRect:
|
||||
drawList.AddRectFilled(
|
||||
ToImVec2Min(command.rect),
|
||||
ToImVec2Max(command.rect),
|
||||
ToImU32(command.color),
|
||||
command.rounding);
|
||||
break;
|
||||
case ::XCEngine::UI::UIDrawCommandType::RectOutline:
|
||||
drawList.AddRect(
|
||||
ToImVec2Min(command.rect),
|
||||
ToImVec2Max(command.rect),
|
||||
ToImU32(command.color),
|
||||
command.rounding,
|
||||
0,
|
||||
command.thickness > 0.0f ? command.thickness : 1.0f);
|
||||
break;
|
||||
case ::XCEngine::UI::UIDrawCommandType::Text:
|
||||
if (!command.text.empty()) {
|
||||
drawList.AddText(
|
||||
nullptr,
|
||||
command.fontSize > 0.0f ? command.fontSize : ImGui::GetFontSize(),
|
||||
ToImVec2(command.position),
|
||||
ToImU32(command.color),
|
||||
command.text.c_str());
|
||||
}
|
||||
break;
|
||||
case ::XCEngine::UI::UIDrawCommandType::Image:
|
||||
if (command.texture.IsValid()) {
|
||||
drawList.AddImage(
|
||||
ToImTextureId(command.texture),
|
||||
ToImVec2Min(command.rect),
|
||||
ToImVec2Max(command.rect),
|
||||
ImVec2(0.0f, 0.0f),
|
||||
ImVec2(1.0f, 1.0f),
|
||||
ToImU32(command.color));
|
||||
}
|
||||
break;
|
||||
case ::XCEngine::UI::UIDrawCommandType::PushClipRect:
|
||||
drawList.PushClipRect(
|
||||
ToImVec2Min(command.rect),
|
||||
ToImVec2Max(command.rect),
|
||||
command.intersectWithCurrentClip);
|
||||
++clipDepth;
|
||||
break;
|
||||
case ::XCEngine::UI::UIDrawCommandType::PopClipRect:
|
||||
if (clipDepth > 0) {
|
||||
drawList.PopClipRect();
|
||||
--clipDepth;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearPendingState() {
|
||||
m_pendingCommandCount = 0;
|
||||
m_pendingDrawLists.clear();
|
||||
}
|
||||
|
||||
std::vector<::XCEngine::UI::UIDrawList> m_pendingDrawLists;
|
||||
std::size_t m_pendingCommandCount = 0;
|
||||
std::size_t m_lastFlushedDrawListCount = 0;
|
||||
std::size_t m_lastFlushedCommandCount = 0;
|
||||
};
|
||||
|
||||
} // namespace XCUIBackend
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
140
editor/src/panels/XCUIDemoPanel.cpp
Normal file
140
editor/src/panels/XCUIDemoPanel.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "XCUIDemoPanel.h"
|
||||
|
||||
#include "UI/UI.h"
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace {
|
||||
|
||||
::XCEngine::UI::UIDrawList BuildXCUIDemoDrawList(
|
||||
const ImVec2& canvasMin,
|
||||
const ImVec2& canvasSize) {
|
||||
using ::XCEngine::UI::UIColor;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
|
||||
UIDrawList drawList("XCUI Transition Demo");
|
||||
drawList.PushClipRect(UIRect(canvasMin.x, canvasMin.y, canvasSize.x, canvasSize.y));
|
||||
drawList.AddFilledRect(
|
||||
UIRect(canvasMin.x, canvasMin.y, canvasSize.x, canvasSize.y),
|
||||
UIColor(0.09f, 0.11f, 0.14f, 1.0f),
|
||||
10.0f);
|
||||
drawList.AddRectOutline(
|
||||
UIRect(canvasMin.x, canvasMin.y, canvasSize.x, canvasSize.y),
|
||||
UIColor(0.32f, 0.40f, 0.49f, 1.0f),
|
||||
1.0f,
|
||||
10.0f);
|
||||
|
||||
const float cardX = canvasMin.x + 18.0f;
|
||||
const float cardY = canvasMin.y + 18.0f;
|
||||
const float cardWidth = (canvasSize.x - 36.0f) > 120.0f ? (canvasSize.x - 36.0f) : 120.0f;
|
||||
drawList.AddFilledRect(
|
||||
UIRect(cardX, cardY, cardWidth, 56.0f),
|
||||
UIColor(0.15f, 0.20f, 0.27f, 1.0f),
|
||||
8.0f);
|
||||
drawList.AddFilledRect(
|
||||
UIRect(cardX + 14.0f, cardY + 16.0f, 10.0f, 10.0f),
|
||||
UIColor(0.18f, 0.76f, 0.58f, 1.0f),
|
||||
5.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 32.0f, cardY + 12.0f),
|
||||
"XCUI Transition Backend",
|
||||
UIColor(0.96f, 0.97f, 0.99f, 1.0f),
|
||||
18.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 32.0f, cardY + 33.0f),
|
||||
"This panel is generated by XCUI draw commands, then flushed through ImGui.",
|
||||
UIColor(0.74f, 0.79f, 0.86f, 1.0f),
|
||||
13.0f);
|
||||
|
||||
const float statY = cardY + 76.0f;
|
||||
drawList.AddFilledRect(
|
||||
UIRect(cardX, statY, 140.0f, 74.0f),
|
||||
UIColor(0.11f, 0.16f, 0.22f, 1.0f),
|
||||
8.0f);
|
||||
drawList.AddFilledRect(
|
||||
UIRect(cardX + 158.0f, statY, 180.0f, 74.0f),
|
||||
UIColor(0.11f, 0.16f, 0.22f, 1.0f),
|
||||
8.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 14.0f, statY + 12.0f),
|
||||
"Primitive Set",
|
||||
UIColor(0.68f, 0.75f, 0.84f, 1.0f),
|
||||
13.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 14.0f, statY + 36.0f),
|
||||
"Rect / Border / Text / Image",
|
||||
UIColor(0.95f, 0.97f, 0.99f, 1.0f),
|
||||
15.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 172.0f, statY + 12.0f),
|
||||
"Backend Flow",
|
||||
UIColor(0.68f, 0.75f, 0.84f, 1.0f),
|
||||
13.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 172.0f, statY + 36.0f),
|
||||
"UIDrawList -> ImGuiTransitionBackend -> ImDrawList",
|
||||
UIColor(0.95f, 0.97f, 0.99f, 1.0f),
|
||||
15.0f);
|
||||
|
||||
const float footerY = canvasMin.y + canvasSize.y - 54.0f;
|
||||
if (footerY > statY + 90.0f) {
|
||||
drawList.AddFilledRect(
|
||||
UIRect(cardX, footerY, cardWidth, 36.0f),
|
||||
UIColor(0.14f, 0.17f, 0.22f, 1.0f),
|
||||
8.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(cardX + 14.0f, footerY + 10.0f),
|
||||
"Subplan-05 MVP: XCUI owns draw data, ImGui is only the transition renderer.",
|
||||
UIColor(0.79f, 0.84f, 0.90f, 1.0f),
|
||||
13.0f);
|
||||
}
|
||||
|
||||
drawList.PopClipRect();
|
||||
return drawList;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
XCUIDemoPanel::XCUIDemoPanel()
|
||||
: Panel("XCUI Demo") {
|
||||
}
|
||||
|
||||
void XCUIDemoPanel::Render() {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
UI::PanelWindowScope panel(m_name.c_str());
|
||||
ImGui::PopStyleVar();
|
||||
if (!panel.IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UI::PanelContentScope content("XCUIDemoContent", ImVec2(0.0f, 0.0f));
|
||||
if (!content.IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ImVec2 availableSize = ImGui::GetContentRegionAvail();
|
||||
if (availableSize.x <= 1.0f || availableSize.y <= 1.0f) {
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
const ImVec2 canvasMin = ImGui::GetCursorScreenPos();
|
||||
ImGui::InvisibleButton("##XCUIDemoCanvas", availableSize);
|
||||
|
||||
::XCEngine::UI::UIDrawData drawData = {};
|
||||
drawData.AddDrawList(BuildXCUIDemoDrawList(canvasMin, availableSize));
|
||||
|
||||
m_backend.BeginFrame();
|
||||
m_backend.Submit(drawData);
|
||||
m_backend.EndFrame(ImGui::GetWindowDrawList());
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
20
editor/src/panels/XCUIDemoPanel.h
Normal file
20
editor/src/panels/XCUIDemoPanel.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Panel.h"
|
||||
|
||||
#include "XCUIBackend/ImGuiTransitionBackend.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class XCUIDemoPanel : public Panel {
|
||||
public:
|
||||
XCUIDemoPanel();
|
||||
void Render() override;
|
||||
|
||||
private:
|
||||
XCUIBackend::ImGuiTransitionBackend m_backend;
|
||||
};
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
204
engine/include/XCEngine/UI/DrawData.h
Normal file
204
engine/include/XCEngine/UI/DrawData.h
Normal file
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
struct UIColor {
|
||||
float r = 1.0f;
|
||||
float g = 1.0f;
|
||||
float b = 1.0f;
|
||||
float a = 1.0f;
|
||||
|
||||
constexpr UIColor() = default;
|
||||
constexpr UIColor(float red, float green, float blue, float alpha = 1.0f)
|
||||
: r(red)
|
||||
, g(green)
|
||||
, b(blue)
|
||||
, a(alpha) {
|
||||
}
|
||||
};
|
||||
|
||||
enum class UIDrawCommandType : std::uint8_t {
|
||||
FilledRect = 0,
|
||||
RectOutline,
|
||||
Text,
|
||||
Image,
|
||||
PushClipRect,
|
||||
PopClipRect
|
||||
};
|
||||
|
||||
struct UIDrawCommand {
|
||||
UIDrawCommandType type = UIDrawCommandType::FilledRect;
|
||||
UIRect rect = {};
|
||||
UIPoint position = {};
|
||||
UIColor color = {};
|
||||
float thickness = 1.0f;
|
||||
float rounding = 0.0f;
|
||||
float fontSize = 0.0f;
|
||||
bool intersectWithCurrentClip = true;
|
||||
UITextureHandle texture = {};
|
||||
std::string text;
|
||||
};
|
||||
|
||||
class UIDrawList {
|
||||
public:
|
||||
UIDrawList() = default;
|
||||
explicit UIDrawList(std::string debugName)
|
||||
: m_debugName(std::move(debugName)) {
|
||||
}
|
||||
|
||||
const std::string& GetDebugName() const {
|
||||
return m_debugName;
|
||||
}
|
||||
|
||||
void SetDebugName(std::string debugName) {
|
||||
m_debugName = std::move(debugName);
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
m_commands.clear();
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return m_commands.empty();
|
||||
}
|
||||
|
||||
std::size_t GetCommandCount() const {
|
||||
return m_commands.size();
|
||||
}
|
||||
|
||||
const std::vector<UIDrawCommand>& GetCommands() const {
|
||||
return m_commands;
|
||||
}
|
||||
|
||||
UIDrawCommand& AddFilledRect(
|
||||
const UIRect& rect,
|
||||
const UIColor& color,
|
||||
float rounding = 0.0f) {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::FilledRect;
|
||||
command.rect = rect;
|
||||
command.color = color;
|
||||
command.rounding = rounding;
|
||||
return AddCommand(std::move(command));
|
||||
}
|
||||
|
||||
UIDrawCommand& AddRectOutline(
|
||||
const UIRect& rect,
|
||||
const UIColor& color,
|
||||
float thickness = 1.0f,
|
||||
float rounding = 0.0f) {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::RectOutline;
|
||||
command.rect = rect;
|
||||
command.color = color;
|
||||
command.thickness = thickness;
|
||||
command.rounding = rounding;
|
||||
return AddCommand(std::move(command));
|
||||
}
|
||||
|
||||
UIDrawCommand& AddText(
|
||||
const UIPoint& position,
|
||||
std::string text,
|
||||
const UIColor& color = {},
|
||||
float fontSize = 0.0f) {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::Text;
|
||||
command.position = position;
|
||||
command.color = color;
|
||||
command.fontSize = fontSize;
|
||||
command.text = std::move(text);
|
||||
return AddCommand(std::move(command));
|
||||
}
|
||||
|
||||
UIDrawCommand& AddImage(
|
||||
const UIRect& rect,
|
||||
const UITextureHandle& texture,
|
||||
const UIColor& tintColor = {}) {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::Image;
|
||||
command.rect = rect;
|
||||
command.texture = texture;
|
||||
command.color = tintColor;
|
||||
return AddCommand(std::move(command));
|
||||
}
|
||||
|
||||
UIDrawCommand& PushClipRect(
|
||||
const UIRect& rect,
|
||||
bool intersectWithCurrentClip = true) {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::PushClipRect;
|
||||
command.rect = rect;
|
||||
command.intersectWithCurrentClip = intersectWithCurrentClip;
|
||||
return AddCommand(std::move(command));
|
||||
}
|
||||
|
||||
UIDrawCommand& PopClipRect() {
|
||||
UIDrawCommand command = {};
|
||||
command.type = UIDrawCommandType::PopClipRect;
|
||||
return AddCommand(std::move(command));
|
||||
}
|
||||
|
||||
private:
|
||||
UIDrawCommand& AddCommand(UIDrawCommand&& command) {
|
||||
m_commands.push_back(std::move(command));
|
||||
return m_commands.back();
|
||||
}
|
||||
|
||||
std::string m_debugName;
|
||||
std::vector<UIDrawCommand> m_commands;
|
||||
};
|
||||
|
||||
class UIDrawData {
|
||||
public:
|
||||
void Clear() {
|
||||
m_drawLists.clear();
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return m_drawLists.empty();
|
||||
}
|
||||
|
||||
std::size_t GetDrawListCount() const {
|
||||
return m_drawLists.size();
|
||||
}
|
||||
|
||||
std::size_t GetTotalCommandCount() const {
|
||||
std::size_t count = 0;
|
||||
for (const UIDrawList& drawList : m_drawLists) {
|
||||
count += drawList.GetCommandCount();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
const std::vector<UIDrawList>& GetDrawLists() const {
|
||||
return m_drawLists;
|
||||
}
|
||||
|
||||
UIDrawList& EmplaceDrawList(std::string debugName = {}) {
|
||||
m_drawLists.emplace_back(std::move(debugName));
|
||||
return m_drawLists.back();
|
||||
}
|
||||
|
||||
void AddDrawList(const UIDrawList& drawList) {
|
||||
m_drawLists.push_back(drawList);
|
||||
}
|
||||
|
||||
void AddDrawList(UIDrawList&& drawList) {
|
||||
m_drawLists.push_back(std::move(drawList));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<UIDrawList> m_drawLists;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
@@ -29,6 +29,8 @@ set(EDITOR_TEST_SOURCES
|
||||
test_viewport_render_targets.cpp
|
||||
test_viewport_render_flow_utils.cpp
|
||||
test_builtin_icon_layout_utils.cpp
|
||||
test_xcui_draw_data.cpp
|
||||
test_xcui_imgui_transition_backend.cpp
|
||||
test_editor_console_sink.cpp
|
||||
test_editor_script_assembly_builder.cpp
|
||||
test_editor_script_assembly_builder_utils.cpp
|
||||
|
||||
53
tests/editor/test_xcui_draw_data.cpp
Normal file
53
tests/editor/test_xcui_draw_data.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
TEST(XCUIDrawDataTest, DrawListBuilderPreservesCommandOrderAndPayload) {
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawCommandType;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UITextureHandle;
|
||||
|
||||
UIDrawList drawList("PrimaryList");
|
||||
drawList.PushClipRect(UIRect(0.0f, 0.0f, 200.0f, 120.0f));
|
||||
drawList.AddFilledRect(UIRect(10.0f, 12.0f, 48.0f, 24.0f), UIColor(0.1f, 0.2f, 0.3f, 1.0f), 4.0f);
|
||||
drawList.AddRectOutline(UIRect(10.0f, 12.0f, 48.0f, 24.0f), UIColor(0.4f, 0.5f, 0.6f, 1.0f), 2.0f, 4.0f);
|
||||
drawList.AddText(UIPoint(18.0f, 22.0f), "XCUI", UIColor(1.0f, 1.0f, 1.0f, 1.0f), 16.0f);
|
||||
drawList.AddImage(
|
||||
UIRect(70.0f, 18.0f, 32.0f, 32.0f),
|
||||
UITextureHandle{ 123u, 32u, 32u },
|
||||
UIColor(0.8f, 0.9f, 1.0f, 1.0f));
|
||||
drawList.PopClipRect();
|
||||
|
||||
ASSERT_EQ(drawList.GetCommandCount(), 6u);
|
||||
const auto& commands = drawList.GetCommands();
|
||||
EXPECT_EQ(commands[0].type, UIDrawCommandType::PushClipRect);
|
||||
EXPECT_EQ(commands[1].type, UIDrawCommandType::FilledRect);
|
||||
EXPECT_EQ(commands[2].type, UIDrawCommandType::RectOutline);
|
||||
EXPECT_EQ(commands[3].type, UIDrawCommandType::Text);
|
||||
EXPECT_EQ(commands[4].type, UIDrawCommandType::Image);
|
||||
EXPECT_EQ(commands[5].type, UIDrawCommandType::PopClipRect);
|
||||
EXPECT_FLOAT_EQ(commands[1].rounding, 4.0f);
|
||||
EXPECT_FLOAT_EQ(commands[2].thickness, 2.0f);
|
||||
EXPECT_EQ(commands[3].text, "XCUI");
|
||||
EXPECT_EQ(commands[4].texture.nativeHandle, 123u);
|
||||
}
|
||||
|
||||
TEST(XCUIDrawDataTest, DrawDataAggregatesMultipleLists) {
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawData;
|
||||
using XCEngine::UI::UIRect;
|
||||
|
||||
UIDrawData drawData = {};
|
||||
auto& first = drawData.EmplaceDrawList("First");
|
||||
first.AddFilledRect(UIRect(0.0f, 0.0f, 20.0f, 20.0f), UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
auto& second = drawData.EmplaceDrawList("Second");
|
||||
second.AddFilledRect(UIRect(5.0f, 5.0f, 10.0f, 10.0f), UIColor(0.0f, 1.0f, 0.0f, 1.0f));
|
||||
second.AddRectOutline(UIRect(5.0f, 5.0f, 10.0f, 10.0f), UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
EXPECT_EQ(drawData.GetDrawListCount(), 2u);
|
||||
EXPECT_EQ(drawData.GetTotalCommandCount(), 3u);
|
||||
}
|
||||
97
tests/editor/test_xcui_imgui_transition_backend.cpp
Normal file
97
tests/editor/test_xcui_imgui_transition_backend.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "XCUIBackend/ImGuiTransitionBackend.h"
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace {
|
||||
|
||||
class ImGuiContextScope {
|
||||
public:
|
||||
ImGuiContextScope() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
}
|
||||
|
||||
~ImGuiContextScope() {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(XCUIImGuiTransitionBackendTest, BeginFrameResetsPendingState) {
|
||||
ImGuiContextScope contextScope;
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("Pending");
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(0.0f, 0.0f, 16.0f, 16.0f),
|
||||
XCEngine::UI::UIColor(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
ASSERT_EQ(backend.GetPendingDrawListCount(), 1u);
|
||||
ASSERT_EQ(backend.GetPendingCommandCount(), 1u);
|
||||
|
||||
backend.BeginFrame();
|
||||
EXPECT_EQ(backend.GetPendingDrawListCount(), 0u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(XCUIImGuiTransitionBackendTest, EndFrameFlushesCommandsToImGuiDrawList) {
|
||||
ImGuiContextScope contextScope;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2(1280.0f, 720.0f);
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
unsigned char* fontPixels = nullptr;
|
||||
int fontWidth = 0;
|
||||
int fontHeight = 0;
|
||||
io.Fonts->GetTexDataAsRGBA32(&fontPixels, &fontWidth, &fontHeight);
|
||||
io.Fonts->SetTexID(static_cast<ImTextureID>(1));
|
||||
|
||||
XCEngine::Editor::XCUIBackend::ImGuiTransitionBackend backend = {};
|
||||
XCEngine::UI::UIDrawList drawList("FlushTest");
|
||||
drawList.PushClipRect(XCEngine::UI::UIRect(0.0f, 0.0f, 220.0f, 160.0f));
|
||||
drawList.AddFilledRect(
|
||||
XCEngine::UI::UIRect(10.0f, 10.0f, 60.0f, 40.0f),
|
||||
XCEngine::UI::UIColor(0.1f, 0.2f, 0.3f, 1.0f),
|
||||
4.0f);
|
||||
drawList.AddRectOutline(
|
||||
XCEngine::UI::UIRect(10.0f, 10.0f, 60.0f, 40.0f),
|
||||
XCEngine::UI::UIColor(0.9f, 0.9f, 0.9f, 1.0f),
|
||||
2.0f,
|
||||
4.0f);
|
||||
drawList.AddText(
|
||||
XCEngine::UI::UIPoint(18.0f, 26.0f),
|
||||
"Backend",
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
16.0f);
|
||||
drawList.AddImage(
|
||||
XCEngine::UI::UIRect(90.0f, 12.0f, 32.0f, 32.0f),
|
||||
XCEngine::UI::UITextureHandle{ 0x100u, 32u, 32u },
|
||||
XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
drawList.PopClipRect();
|
||||
|
||||
backend.BeginFrame();
|
||||
backend.Submit(drawList);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ASSERT_TRUE(ImGui::Begin("XCUIBackendTest"));
|
||||
ImDrawList* targetDrawList = ImGui::GetWindowDrawList();
|
||||
ASSERT_NE(targetDrawList, nullptr);
|
||||
|
||||
const bool flushed = backend.EndFrame(targetDrawList);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndFrame();
|
||||
|
||||
EXPECT_TRUE(flushed);
|
||||
EXPECT_EQ(backend.GetLastFlushedDrawListCount(), 1u);
|
||||
EXPECT_EQ(backend.GetLastFlushedCommandCount(), 6u);
|
||||
EXPECT_EQ(backend.GetPendingCommandCount(), 0u);
|
||||
EXPECT_GT(targetDrawList->VtxBuffer.Size, 0);
|
||||
EXPECT_GT(targetDrawList->CmdBuffer.Size, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user