Extract XCUI panel canvas host seam
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "XCUIDemoPanel.h"
|
||||
|
||||
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
||||
#include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h"
|
||||
#include "XCUIBackend/ImGuiXCUIInputAdapter.h"
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
@@ -20,27 +21,9 @@ namespace {
|
||||
|
||||
constexpr float kCanvasHudHeight = 82.0f;
|
||||
constexpr float kCanvasHudPadding = 10.0f;
|
||||
constexpr ImU32 kPreviewFrameColor = IM_COL32(54, 72, 94, 255);
|
||||
constexpr ImU32 kPreviewPlaceholderFill = IM_COL32(18, 24, 32, 255);
|
||||
constexpr ImU32 kPreviewPlaceholderText = IM_COL32(191, 205, 224, 255);
|
||||
constexpr ImU32 kPreviewPlaceholderSubtleText = IM_COL32(132, 147, 170, 255);
|
||||
constexpr char kPreviewDebugName[] = "XCUI Demo";
|
||||
constexpr char kPreviewDebugSource[] = "new_editor.panels.xcui_demo";
|
||||
|
||||
UI::UIRect ToUIRect(const ImVec2& minPoint, const ImVec2& size) {
|
||||
return UI::UIRect(minPoint.x, minPoint.y, size.x, size.y);
|
||||
}
|
||||
|
||||
ImTextureID ToImTextureId(const UI::UITextureHandle& texture) {
|
||||
return texture.IsValid()
|
||||
? static_cast<ImTextureID>(texture.nativeHandle)
|
||||
: ImTextureID{};
|
||||
}
|
||||
|
||||
ImVec2 ToImVec2(const UI::UIPoint& point) {
|
||||
return ImVec2(point.x, point.y);
|
||||
}
|
||||
|
||||
bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
|
||||
return point.x >= rect.x &&
|
||||
point.y >= rect.y &&
|
||||
@@ -48,47 +31,13 @@ bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
|
||||
point.y <= rect.y + rect.height;
|
||||
}
|
||||
|
||||
void DrawHostedPreviewFrame(
|
||||
ImDrawList* drawList,
|
||||
const ImVec2& minPoint,
|
||||
const ImVec2& size) {
|
||||
if (drawList == nullptr || size.x <= 1.0f || size.y <= 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ImVec2 maxPoint(minPoint.x + size.x, minPoint.y + size.y);
|
||||
drawList->AddRect(minPoint, maxPoint, kPreviewFrameColor, 8.0f, 0, 1.0f);
|
||||
}
|
||||
|
||||
void DrawHostedPreviewPlaceholder(
|
||||
ImDrawList* drawList,
|
||||
const ImVec2& minPoint,
|
||||
const ImVec2& size,
|
||||
const char* title,
|
||||
const char* subtitle) {
|
||||
if (drawList == nullptr || size.x <= 1.0f || size.y <= 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ImVec2 maxPoint(minPoint.x + size.x, minPoint.y + size.y);
|
||||
drawList->AddRectFilled(minPoint, maxPoint, kPreviewPlaceholderFill, 8.0f);
|
||||
drawList->AddRect(minPoint, maxPoint, kPreviewFrameColor, 8.0f, 0, 1.0f);
|
||||
drawList->AddText(ImVec2(minPoint.x + 14.0f, minPoint.y + 14.0f), kPreviewPlaceholderText, title);
|
||||
if (subtitle != nullptr && subtitle[0] != '\0') {
|
||||
drawList->AddText(
|
||||
ImVec2(minPoint.x + 14.0f, minPoint.y + 36.0f),
|
||||
kPreviewPlaceholderSubtleText,
|
||||
subtitle);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRectOverlay(
|
||||
ImDrawList* drawList,
|
||||
::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost& canvasHost,
|
||||
::XCEngine::Editor::XCUIBackend::XCUIDemoRuntime& runtime,
|
||||
const std::string& elementId,
|
||||
ImU32 color,
|
||||
const UI::UIColor& color,
|
||||
const char* label) {
|
||||
if (drawList == nullptr || elementId.empty()) {
|
||||
if (elementId.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,11 +46,12 @@ void DrawRectOverlay(
|
||||
return;
|
||||
}
|
||||
|
||||
const ImVec2 minPoint(rect.x, rect.y);
|
||||
const ImVec2 maxPoint(rect.x + rect.width, rect.y + rect.height);
|
||||
drawList->AddRect(minPoint, maxPoint, color, 6.0f, 0, 2.0f);
|
||||
canvasHost.DrawOutlineRect(rect, color, 2.0f, 6.0f);
|
||||
if (label != nullptr && label[0] != '\0') {
|
||||
drawList->AddText(ImVec2(minPoint.x + 4.0f, minPoint.y + 4.0f), color, label);
|
||||
canvasHost.DrawText(
|
||||
UI::UIPoint(rect.x + 4.0f, rect.y + 4.0f),
|
||||
label,
|
||||
color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +87,8 @@ XCUIDemoPanel::XCUIDemoPanel(
|
||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter)
|
||||
: Panel("XCUI Demo")
|
||||
, m_inputSource(inputSource)
|
||||
, m_previewPresenter(std::move(previewPresenter)) {
|
||||
, m_previewPresenter(std::move(previewPresenter))
|
||||
, m_canvasHost(::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()) {
|
||||
if (m_previewPresenter == nullptr) {
|
||||
m_previewPresenter = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter();
|
||||
}
|
||||
@@ -168,6 +119,14 @@ void XCUIDemoPanel::SetHostedPreviewPresenter(
|
||||
m_lastPreviewStats = {};
|
||||
}
|
||||
|
||||
void XCUIDemoPanel::SetCanvasHost(
|
||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost> canvasHost) {
|
||||
m_canvasHost = std::move(canvasHost);
|
||||
if (m_canvasHost == nullptr) {
|
||||
m_canvasHost = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost();
|
||||
}
|
||||
}
|
||||
|
||||
bool XCUIDemoPanel::IsUsingNativeHostedPreview() const {
|
||||
return m_previewPresenter != nullptr && m_previewPresenter->IsNativeQueued();
|
||||
}
|
||||
@@ -200,15 +159,10 @@ void XCUIDemoPanel::Render() {
|
||||
const ImVec2 hostRegion = ImGui::GetContentRegionAvail();
|
||||
const float canvasHeight = (std::max)(140.0f, hostRegion.y - diagnosticsHeight);
|
||||
|
||||
ImGui::BeginChild("XCUIDemoCanvasHost", ImVec2(0.0f, canvasHeight), true, ImGuiWindowFlags_NoScrollWithMouse);
|
||||
const ImVec2 canvasHostMin = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 availableSize = ImGui::GetContentRegionAvail();
|
||||
const float topInset = m_showCanvasHud ? (kCanvasHudHeight + kCanvasHudPadding) : 0.0f;
|
||||
const ImVec2 canvasMin(canvasHostMin.x, canvasHostMin.y + topInset);
|
||||
const ImVec2 canvasSize(
|
||||
availableSize.x,
|
||||
(std::max)(0.0f, availableSize.y - topInset));
|
||||
const bool validCanvas = canvasSize.x > 1.0f && canvasSize.y > 1.0f;
|
||||
if (m_canvasHost == nullptr) {
|
||||
m_canvasHost = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost();
|
||||
}
|
||||
|
||||
const bool nativeHostedPreview = IsUsingNativeHostedPreview();
|
||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
|
||||
const bool hasHostedSurfaceDescriptor =
|
||||
@@ -217,38 +171,27 @@ void XCUIDemoPanel::Render() {
|
||||
m_previewPresenter->TryGetSurfaceDescriptor(kPreviewDebugName, hostedSurfaceDescriptor);
|
||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
|
||||
const bool showHostedSurfaceImage =
|
||||
validCanvas &&
|
||||
nativeHostedPreview &&
|
||||
m_previewPresenter != nullptr &&
|
||||
m_previewPresenter->TryGetSurfaceImage(kPreviewDebugName, hostedSurfaceImage);
|
||||
const char* const previewPathLabel = GetPreviewPathLabel(nativeHostedPreview);
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
if (validCanvas) {
|
||||
ImGui::SetCursorScreenPos(canvasMin);
|
||||
if (showHostedSurfaceImage) {
|
||||
ImGui::Image(
|
||||
ToImTextureId(hostedSurfaceImage.texture),
|
||||
canvasSize,
|
||||
ToImVec2(hostedSurfaceImage.uvMin),
|
||||
ToImVec2(hostedSurfaceImage.uvMax));
|
||||
DrawHostedPreviewFrame(drawList, canvasMin, canvasSize);
|
||||
} else {
|
||||
ImGui::InvisibleButton("##XCUIDemoCanvas", canvasSize);
|
||||
const char* placeholderSubtitle =
|
||||
nativeHostedPreview
|
||||
? "Waiting for native queued render output to publish back into the sandbox panel."
|
||||
: "Legacy ImGui transition path stays active until native offscreen preview is enabled.";
|
||||
DrawHostedPreviewPlaceholder(
|
||||
drawList,
|
||||
canvasMin,
|
||||
canvasSize,
|
||||
nativeHostedPreview ? "Native XCUI preview pending" : "Legacy XCUI canvas host",
|
||||
placeholderSubtitle);
|
||||
}
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
}
|
||||
const float topInset = m_showCanvasHud ? (kCanvasHudHeight + kCanvasHudPadding) : 0.0f;
|
||||
::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasRequest canvasRequest = {};
|
||||
canvasRequest.childId = "XCUIDemoCanvasHost";
|
||||
canvasRequest.height = canvasHeight;
|
||||
canvasRequest.topInset = topInset;
|
||||
canvasRequest.showSurfaceImage = showHostedSurfaceImage;
|
||||
canvasRequest.surfaceImage = hostedSurfaceImage;
|
||||
canvasRequest.placeholderTitle =
|
||||
nativeHostedPreview ? "Native XCUI preview pending" : "Legacy XCUI canvas host";
|
||||
canvasRequest.placeholderSubtitle =
|
||||
nativeHostedPreview
|
||||
? "Waiting for native queued render output to publish back into the sandbox panel."
|
||||
: "Legacy ImGui transition path stays active until native offscreen preview is enabled.";
|
||||
const ::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession canvasSession =
|
||||
m_canvasHost->BeginCanvas(canvasRequest);
|
||||
const UI::UIRect canvasRect = canvasSession.canvasRect;
|
||||
const bool validCanvas = canvasSession.validCanvas;
|
||||
|
||||
::XCEngine::Editor::XCUIBackend::XCUIDemoInputState input = {};
|
||||
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions bridgeOptions = {};
|
||||
@@ -256,22 +199,22 @@ void XCUIDemoPanel::Render() {
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count());
|
||||
const UI::UIRect canvasRect = ToUIRect(canvasMin, canvasSize);
|
||||
|
||||
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot snapshot = {};
|
||||
if (m_inputSource != nullptr) {
|
||||
bridgeOptions.hasPointerInsideOverride = true;
|
||||
bridgeOptions.windowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||
bridgeOptions.windowFocused = canvasSession.windowFocused;
|
||||
const UI::UIPoint pointerPosition = m_inputSource->GetPointerPosition();
|
||||
bridgeOptions.pointerInsideOverride = validCanvas && ContainsPoint(canvasRect, pointerPosition);
|
||||
snapshot = m_inputSource->CaptureSnapshot(bridgeOptions);
|
||||
} else {
|
||||
bridgeOptions.hasPointerInsideOverride = true;
|
||||
bridgeOptions.pointerInsideOverride = validCanvas && ImGui::IsItemHovered();
|
||||
bridgeOptions.windowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||
bridgeOptions.pointerInsideOverride = validCanvas && canvasSession.hovered;
|
||||
bridgeOptions.windowFocused = canvasSession.windowFocused;
|
||||
snapshot = ::XCEngine::Editor::XCUIBackend::ImGuiXCUIInputAdapter::CaptureSnapshot(
|
||||
ImGui::GetIO(),
|
||||
bridgeOptions);
|
||||
snapshot.pointerPosition = canvasSession.pointerPosition;
|
||||
}
|
||||
|
||||
if (!m_inputBridge.HasBaseline()) {
|
||||
@@ -315,48 +258,64 @@ void XCUIDemoPanel::Render() {
|
||||
hasHostedSurfaceDescriptor,
|
||||
showHostedSurfaceImage);
|
||||
if (m_showCanvasHud) {
|
||||
const ImVec2 hudMin(canvasHostMin.x + 8.0f, canvasHostMin.y + 8.0f);
|
||||
const ImVec2 hudMax(
|
||||
canvasHostMin.x + (std::min)(availableSize.x - 8.0f, 430.0f),
|
||||
canvasHostMin.y + kCanvasHudHeight);
|
||||
drawList->AddRectFilled(
|
||||
hudMin,
|
||||
hudMax,
|
||||
IM_COL32(16, 22, 30, 220),
|
||||
const float hudWidth = (std::min)(canvasSession.hostRect.width - 16.0f, 430.0f);
|
||||
const UI::UIRect hudRect(
|
||||
canvasSession.hostRect.x + 8.0f,
|
||||
canvasSession.hostRect.y + 8.0f,
|
||||
(std::max)(0.0f, hudWidth),
|
||||
kCanvasHudHeight - 8.0f);
|
||||
m_canvasHost->DrawFilledRect(
|
||||
hudRect,
|
||||
UI::UIColor(16.0f / 255.0f, 22.0f / 255.0f, 30.0f / 255.0f, 220.0f / 255.0f),
|
||||
8.0f);
|
||||
m_canvasHost->DrawOutlineRect(
|
||||
hudRect,
|
||||
UI::UIColor(52.0f / 255.0f, 72.0f / 255.0f, 96.0f / 255.0f, 1.0f),
|
||||
1.0f,
|
||||
8.0f);
|
||||
drawList->AddRect(
|
||||
hudMin,
|
||||
hudMax,
|
||||
IM_COL32(52, 72, 96, 255),
|
||||
8.0f,
|
||||
0,
|
||||
1.0f);
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(hudMin.x + 10.0f, hudMin.y + 8.0f));
|
||||
ImGui::BeginGroup();
|
||||
ImGui::TextUnformatted("XCUI Runtime");
|
||||
ImGui::Text("%s | %s", previewPathLabel, previewStateLabel);
|
||||
ImGui::TextUnformatted(stats.statusMessage.c_str());
|
||||
ImGui::Text(
|
||||
"Tree %llu | Elements %zu | Commands %zu",
|
||||
static_cast<unsigned long long>(stats.treeGeneration),
|
||||
stats.elementCount,
|
||||
stats.commandCount);
|
||||
ImGui::Text(
|
||||
"Submit %zu/%zu | Flush %zu/%zu",
|
||||
m_lastPreviewStats.submittedDrawListCount,
|
||||
m_lastPreviewStats.submittedCommandCount,
|
||||
m_lastPreviewStats.flushedDrawListCount,
|
||||
m_lastPreviewStats.flushedCommandCount);
|
||||
ImGui::EndGroup();
|
||||
const UI::UIPoint lineOrigin(hudRect.x + 10.0f, hudRect.y + 8.0f);
|
||||
const UI::UIColor textColor(191.0f / 255.0f, 205.0f / 255.0f, 224.0f / 255.0f, 1.0f);
|
||||
m_canvasHost->DrawText(lineOrigin, "XCUI Runtime", UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
std::string previewLine = std::string(previewPathLabel) + " | " + previewStateLabel;
|
||||
std::string treeLine =
|
||||
"Tree " + std::to_string(static_cast<unsigned long long>(stats.treeGeneration)) +
|
||||
" | Elements " + std::to_string(stats.elementCount) +
|
||||
" | Commands " + std::to_string(stats.commandCount);
|
||||
std::string flushLine =
|
||||
"Submit " + std::to_string(m_lastPreviewStats.submittedDrawListCount) +
|
||||
"/" + std::to_string(m_lastPreviewStats.submittedCommandCount) +
|
||||
" | Flush " + std::to_string(m_lastPreviewStats.flushedDrawListCount) +
|
||||
"/" + std::to_string(m_lastPreviewStats.flushedCommandCount);
|
||||
|
||||
m_canvasHost->DrawText(UI::UIPoint(lineOrigin.x, lineOrigin.y + 18.0f), previewLine, textColor);
|
||||
m_canvasHost->DrawText(UI::UIPoint(lineOrigin.x, lineOrigin.y + 36.0f), stats.statusMessage, textColor);
|
||||
m_canvasHost->DrawText(UI::UIPoint(lineOrigin.x, lineOrigin.y + 54.0f), treeLine, textColor);
|
||||
m_canvasHost->DrawText(UI::UIPoint(lineOrigin.x + 210.0f, lineOrigin.y + 54.0f), flushLine, textColor);
|
||||
}
|
||||
|
||||
if (m_showDebugRects && validCanvas && (!nativeHostedPreview || showHostedSurfaceImage)) {
|
||||
DrawRectOverlay(drawList, m_runtime, stats.hoveredElementId, IM_COL32(255, 195, 64, 255), "hover");
|
||||
DrawRectOverlay(drawList, m_runtime, stats.focusedElementId, IM_COL32(64, 214, 255, 255), "focus");
|
||||
DrawRectOverlay(drawList, m_runtime, "toggleAccent", IM_COL32(150, 255, 150, 160), "toggle");
|
||||
DrawRectOverlay(
|
||||
*m_canvasHost,
|
||||
m_runtime,
|
||||
stats.hoveredElementId,
|
||||
UI::UIColor(1.0f, 195.0f / 255.0f, 64.0f / 255.0f, 1.0f),
|
||||
"hover");
|
||||
DrawRectOverlay(
|
||||
*m_canvasHost,
|
||||
m_runtime,
|
||||
stats.focusedElementId,
|
||||
UI::UIColor(64.0f / 255.0f, 214.0f / 255.0f, 1.0f, 1.0f),
|
||||
"focus");
|
||||
DrawRectOverlay(
|
||||
*m_canvasHost,
|
||||
m_runtime,
|
||||
"toggleAccent",
|
||||
UI::UIColor(150.0f / 255.0f, 1.0f, 150.0f / 255.0f, 160.0f / 255.0f),
|
||||
"toggle");
|
||||
}
|
||||
ImGui::EndChild();
|
||||
m_canvasHost->EndCanvas();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::BeginChild("XCUIDemoDiagnostics", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_NoScrollbar);
|
||||
|
||||
Reference in New Issue
Block a user