Extract XCUI panel canvas host seam
This commit is contained in:
@@ -74,6 +74,7 @@ Current gap:
|
|||||||
- `LayoutLab` now also consumes the shared `UIExpansionModel` for tree expansion and property-section collapse, with reserved property headers, disclosure glyphs, and persisted click-toggle behavior in the sandbox runtime.
|
- `LayoutLab` now also consumes the shared `UIExpansionModel` for tree expansion and property-section collapse, with reserved property headers, disclosure glyphs, and persisted click-toggle behavior in the sandbox runtime.
|
||||||
- `new_editor` now also has an isolated `XCUIEditorCommandRouter` model with shortcut matching, enable predicates, and direct command invocation semantics covered by dedicated tests, ready for shell-frame integration.
|
- `new_editor` now also has an isolated `XCUIEditorCommandRouter` model with shortcut matching, enable predicates, and direct command invocation semantics covered by dedicated tests, ready for shell-frame integration.
|
||||||
- `XCUI Demo` now exports pending per-frame command ids through `DrainPendingCommandIds()`, so editor-side hosts have a clean seam for observing demo/runtime command traffic without parsing draw data.
|
- `XCUI Demo` now exports pending per-frame command ids through `DrainPendingCommandIds()`, so editor-side hosts have a clean seam for observing demo/runtime command traffic without parsing draw data.
|
||||||
|
- `XCUI Demo` and `LayoutLab` panel canvases are now being pulled behind a dedicated `IXCUIPanelCanvasHost` seam, so canvas surface presentation, hover/focus fallback state, and overlay draw hooks no longer have to stay hard-coded inside each ImGui panel implementation.
|
||||||
- Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths.
|
- Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths.
|
||||||
- The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration.
|
- The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration.
|
||||||
- `Application` no longer owns the ImGui backend directly; window presentation now routes through `IWindowUICompositor` with an `ImGuiWindowUICompositor` implementation, which currently delegates to `IEditorHostCompositor` / `ImGuiHostCompositor`.
|
- `Application` no longer owns the ImGui backend directly; window presentation now routes through `IWindowUICompositor` with an `ImGuiWindowUICompositor` implementation, which currently delegates to `IEditorHostCompositor` / `ImGuiHostCompositor`.
|
||||||
@@ -86,6 +87,7 @@ Current gap:
|
|||||||
|
|
||||||
- The shell is still ImGui-hosted.
|
- The shell is still ImGui-hosted.
|
||||||
- Legacy hosted preview still depends on an active ImGui window context for inline presentation.
|
- Legacy hosted preview still depends on an active ImGui window context for inline presentation.
|
||||||
|
- The new panel-canvas seam still only has an ImGui adapter today; a native panel/shell host still needs to replace it before ImGui can stop being the default editor host path.
|
||||||
- Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, and icon-atlas widgets are not yet extracted into reusable XCUI modules, and the new keyboard-navigation/property-edit/command-routing models are still only partially integrated.
|
- Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, and icon-atlas widgets are not yet extracted into reusable XCUI modules, and the new keyboard-navigation/property-edit/command-routing models are still only partially integrated.
|
||||||
|
|
||||||
## Validated This Phase
|
## Validated This Phase
|
||||||
@@ -172,6 +174,7 @@ Current gap:
|
|||||||
- The window-level XCUI compositor seam now also has a dedicated regression target around `ImGuiWindowUICompositor`, covering initialization, render-frame ordering, Win32 message forwarding, texture registration forwarding, and shutdown safety.
|
- The window-level XCUI compositor seam now also has a dedicated regression target around `ImGuiWindowUICompositor`, covering initialization, render-frame ordering, Win32 message forwarding, texture registration forwarding, and shutdown safety.
|
||||||
- The window compositor and hosted-preview seams gained more edge-case coverage around no-UI render passes, compositor re-initialization/rebinding, partial logical-size fallback, and descriptor reuse for repeated queued-frame keys.
|
- The window compositor and hosted-preview seams gained more edge-case coverage around no-UI render passes, compositor re-initialization/rebinding, partial logical-size fallback, and descriptor reuse for repeated queued-frame keys.
|
||||||
- `new_editor` now has a dedicated `XCUIEditorCommandRouter` test target covering command registration, replacement, enable predicates, accelerator matching, and policy gates around focus/keyboard capture/text input.
|
- `new_editor` now has a dedicated `XCUIEditorCommandRouter` test target covering command registration, replacement, enable predicates, accelerator matching, and policy gates around focus/keyboard capture/text input.
|
||||||
|
- `new_editor` panel canvas ownership is now being split behind `IXCUIPanelCanvasHost`, with an `ImGuiXCUIPanelCanvasHost` adapter carrying the legacy path so panel code stops directly owning `ImGui::Image` / `ImGui::InvisibleButton` / draw-list preview plumbing.
|
||||||
- `SceneRuntime` layered XCUI routing now has dedicated regression coverage for:
|
- `SceneRuntime` layered XCUI routing now has dedicated regression coverage for:
|
||||||
- top-interactive layer input ownership
|
- top-interactive layer input ownership
|
||||||
- blocking/modal layer suppression of lower layers
|
- blocking/modal layer suppression of lower layers
|
||||||
|
|||||||
244
new_editor/src/XCUIBackend/ImGuiXCUIPanelCanvasHost.h
Normal file
244
new_editor/src/XCUIBackend/ImGuiXCUIPanelCanvasHost.h
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "XCUIBackend/XCUIPanelCanvasHost.h"
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Editor {
|
||||||
|
namespace XCUIBackend {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline ImVec2 ToImVec2(const ::XCEngine::UI::UIPoint& point) {
|
||||||
|
return ImVec2(point.x, point.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImU32 ToImU32(const ::XCEngine::UI::UIColor& color) {
|
||||||
|
return ImGui::ColorConvertFloat4ToU32(ImVec4(color.r, color.g, color.b, color.a));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImTextureID ToImTextureId(const ::XCEngine::UI::UITextureHandle& texture) {
|
||||||
|
return texture.IsValid()
|
||||||
|
? static_cast<ImTextureID>(texture.nativeHandle)
|
||||||
|
: ImTextureID{};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DrawPlaceholder(
|
||||||
|
ImDrawList* drawList,
|
||||||
|
const ::XCEngine::UI::UIRect& rect,
|
||||||
|
const char* title,
|
||||||
|
const char* subtitle) {
|
||||||
|
if (drawList == nullptr || rect.width <= 1.0f || rect.height <= 1.0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec2 minPoint(rect.x, rect.y);
|
||||||
|
const ImVec2 maxPoint(rect.x + rect.width, rect.y + rect.height);
|
||||||
|
drawList->AddRectFilled(minPoint, maxPoint, IM_COL32(18, 24, 32, 255), 8.0f);
|
||||||
|
drawList->AddRect(minPoint, maxPoint, IM_COL32(54, 72, 94, 255), 8.0f, 0, 1.0f);
|
||||||
|
|
||||||
|
if (title != nullptr && title[0] != '\0') {
|
||||||
|
drawList->AddText(ImVec2(rect.x + 14.0f, rect.y + 14.0f), IM_COL32(191, 205, 224, 255), title);
|
||||||
|
}
|
||||||
|
if (subtitle != nullptr && subtitle[0] != '\0') {
|
||||||
|
drawList->AddText(
|
||||||
|
ImVec2(rect.x + 14.0f, rect.y + 36.0f),
|
||||||
|
IM_COL32(132, 147, 170, 255),
|
||||||
|
subtitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DrawBadge(
|
||||||
|
ImDrawList* drawList,
|
||||||
|
const ::XCEngine::UI::UIRect& canvasRect,
|
||||||
|
const char* title,
|
||||||
|
const char* subtitle) {
|
||||||
|
if (drawList == nullptr || title == nullptr || title[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ::XCEngine::UI::UIRect badgeRect(
|
||||||
|
canvasRect.x + 10.0f,
|
||||||
|
canvasRect.y + 10.0f,
|
||||||
|
290.0f,
|
||||||
|
42.0f);
|
||||||
|
const ImVec2 minPoint(badgeRect.x, badgeRect.y);
|
||||||
|
const ImVec2 maxPoint(badgeRect.x + badgeRect.width, badgeRect.y + badgeRect.height);
|
||||||
|
drawList->AddRectFilled(minPoint, maxPoint, IM_COL32(16, 22, 30, 216), 8.0f);
|
||||||
|
drawList->AddRect(minPoint, maxPoint, IM_COL32(52, 72, 96, 255), 8.0f, 0, 1.0f);
|
||||||
|
drawList->AddText(
|
||||||
|
ImVec2(badgeRect.x + 10.0f, badgeRect.y + 8.0f),
|
||||||
|
IM_COL32(191, 205, 224, 255),
|
||||||
|
title);
|
||||||
|
if (subtitle != nullptr && subtitle[0] != '\0') {
|
||||||
|
drawList->AddText(
|
||||||
|
ImVec2(badgeRect.x + 10.0f, badgeRect.y + 24.0f),
|
||||||
|
IM_COL32(132, 147, 170, 255),
|
||||||
|
subtitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
class ImGuiXCUIPanelCanvasHost final : public IXCUIPanelCanvasHost {
|
||||||
|
public:
|
||||||
|
XCUIPanelCanvasSession BeginCanvas(const XCUIPanelCanvasRequest& request) override {
|
||||||
|
const char* childId =
|
||||||
|
request.childId != nullptr && request.childId[0] != '\0'
|
||||||
|
? request.childId
|
||||||
|
: "XCUIPanelCanvasHost";
|
||||||
|
ImGui::BeginChild(
|
||||||
|
childId,
|
||||||
|
ImVec2(0.0f, request.height),
|
||||||
|
request.bordered,
|
||||||
|
ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
|
|
||||||
|
m_drawList = ImGui::GetWindowDrawList();
|
||||||
|
m_canvasSession = {};
|
||||||
|
|
||||||
|
const ImVec2 canvasMin = ImGui::GetCursorScreenPos();
|
||||||
|
const ImVec2 canvasSize = ImGui::GetContentRegionAvail();
|
||||||
|
m_canvasSession.hostRect = ::XCEngine::UI::UIRect(
|
||||||
|
canvasMin.x,
|
||||||
|
canvasMin.y,
|
||||||
|
canvasSize.x,
|
||||||
|
canvasSize.y);
|
||||||
|
const float topInset = request.topInset > 0.0f ? request.topInset : 0.0f;
|
||||||
|
m_canvasSession.canvasRect = ::XCEngine::UI::UIRect(
|
||||||
|
m_canvasSession.hostRect.x,
|
||||||
|
m_canvasSession.hostRect.y + topInset,
|
||||||
|
m_canvasSession.hostRect.width,
|
||||||
|
(m_canvasSession.hostRect.height > topInset)
|
||||||
|
? (m_canvasSession.hostRect.height - topInset)
|
||||||
|
: 0.0f);
|
||||||
|
m_canvasSession.validCanvas =
|
||||||
|
m_canvasSession.canvasRect.width > 1.0f &&
|
||||||
|
m_canvasSession.canvasRect.height > 1.0f;
|
||||||
|
m_canvasSession.windowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||||
|
m_canvasSession.pointerPosition =
|
||||||
|
::XCEngine::UI::UIPoint(ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y);
|
||||||
|
|
||||||
|
if (m_canvasSession.validCanvas) {
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(m_canvasSession.canvasRect.x, m_canvasSession.canvasRect.y));
|
||||||
|
if (request.showSurfaceImage && request.surfaceImage.IsValid()) {
|
||||||
|
ImGui::Image(
|
||||||
|
detail::ToImTextureId(request.surfaceImage.texture),
|
||||||
|
ImVec2(m_canvasSession.canvasRect.width, m_canvasSession.canvasRect.height),
|
||||||
|
detail::ToImVec2(request.surfaceImage.uvMin),
|
||||||
|
detail::ToImVec2(request.surfaceImage.uvMax));
|
||||||
|
} else {
|
||||||
|
std::string buttonId = "##";
|
||||||
|
buttonId += childId;
|
||||||
|
buttonId += ".canvas";
|
||||||
|
ImGui::InvisibleButton(
|
||||||
|
buttonId.c_str(),
|
||||||
|
ImVec2(m_canvasSession.canvasRect.width, m_canvasSession.canvasRect.height));
|
||||||
|
detail::DrawPlaceholder(
|
||||||
|
m_drawList,
|
||||||
|
m_canvasSession.canvasRect,
|
||||||
|
request.placeholderTitle,
|
||||||
|
request.placeholderSubtitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_canvasSession.hovered = ImGui::IsItemHovered();
|
||||||
|
if (request.showSurfaceImage && request.drawPreviewFrame) {
|
||||||
|
DrawOutlineRect(
|
||||||
|
m_canvasSession.canvasRect,
|
||||||
|
::XCEngine::UI::UIColor(54.0f / 255.0f, 72.0f / 255.0f, 94.0f / 255.0f, 1.0f),
|
||||||
|
1.0f,
|
||||||
|
8.0f);
|
||||||
|
}
|
||||||
|
detail::DrawBadge(
|
||||||
|
m_drawList,
|
||||||
|
m_canvasSession.canvasRect,
|
||||||
|
request.badgeTitle,
|
||||||
|
request.badgeSubtitle);
|
||||||
|
} else {
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_canvasSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawFilledRect(
|
||||||
|
const ::XCEngine::UI::UIRect& rect,
|
||||||
|
const ::XCEngine::UI::UIColor& color,
|
||||||
|
float rounding = 0.0f) override {
|
||||||
|
if (m_drawList == nullptr || rect.width <= 0.0f || rect.height <= 0.0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_drawList->AddRectFilled(
|
||||||
|
ImVec2(rect.x, rect.y),
|
||||||
|
ImVec2(rect.x + rect.width, rect.y + rect.height),
|
||||||
|
detail::ToImU32(color),
|
||||||
|
rounding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawOutlineRect(
|
||||||
|
const ::XCEngine::UI::UIRect& rect,
|
||||||
|
const ::XCEngine::UI::UIColor& color,
|
||||||
|
float thickness = 1.0f,
|
||||||
|
float rounding = 0.0f) override {
|
||||||
|
if (m_drawList == nullptr || rect.width <= 0.0f || rect.height <= 0.0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_drawList->AddRect(
|
||||||
|
ImVec2(rect.x, rect.y),
|
||||||
|
ImVec2(rect.x + rect.width, rect.y + rect.height),
|
||||||
|
detail::ToImU32(color),
|
||||||
|
rounding,
|
||||||
|
0,
|
||||||
|
thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawText(
|
||||||
|
const ::XCEngine::UI::UIPoint& position,
|
||||||
|
std::string_view text,
|
||||||
|
const ::XCEngine::UI::UIColor& color,
|
||||||
|
float fontSize = 0.0f) override {
|
||||||
|
if (m_drawList == nullptr || text.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec2 textPosition(position.x, position.y);
|
||||||
|
if (fontSize > 0.0f) {
|
||||||
|
m_drawList->AddText(
|
||||||
|
ImGui::GetFont(),
|
||||||
|
fontSize,
|
||||||
|
textPosition,
|
||||||
|
detail::ToImU32(color),
|
||||||
|
text.data(),
|
||||||
|
text.data() + text.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_drawList->AddText(
|
||||||
|
textPosition,
|
||||||
|
detail::ToImU32(color),
|
||||||
|
text.data(),
|
||||||
|
text.data() + text.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndCanvas() override {
|
||||||
|
ImGui::EndChild();
|
||||||
|
m_drawList = nullptr;
|
||||||
|
m_canvasSession = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImDrawList* m_drawList = nullptr;
|
||||||
|
XCUIPanelCanvasSession m_canvasSession = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<IXCUIPanelCanvasHost> CreateImGuiXCUIPanelCanvasHost() {
|
||||||
|
return std::make_unique<ImGuiXCUIPanelCanvasHost>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace XCUIBackend
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace XCEngine
|
||||||
63
new_editor/src/XCUIBackend/XCUIPanelCanvasHost.h
Normal file
63
new_editor/src/XCUIBackend/XCUIPanelCanvasHost.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
||||||
|
|
||||||
|
#include <XCEngine/UI/DrawData.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Editor {
|
||||||
|
namespace XCUIBackend {
|
||||||
|
|
||||||
|
struct XCUIPanelCanvasRequest {
|
||||||
|
const char* childId = nullptr;
|
||||||
|
float height = 0.0f;
|
||||||
|
float topInset = 0.0f;
|
||||||
|
bool bordered = true;
|
||||||
|
bool showSurfaceImage = false;
|
||||||
|
bool drawPreviewFrame = true;
|
||||||
|
const char* placeholderTitle = nullptr;
|
||||||
|
const char* placeholderSubtitle = nullptr;
|
||||||
|
const char* badgeTitle = nullptr;
|
||||||
|
const char* badgeSubtitle = nullptr;
|
||||||
|
XCUIHostedPreviewSurfaceImage surfaceImage = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XCUIPanelCanvasSession {
|
||||||
|
::XCEngine::UI::UIRect hostRect = {};
|
||||||
|
::XCEngine::UI::UIRect canvasRect = {};
|
||||||
|
::XCEngine::UI::UIPoint pointerPosition = {};
|
||||||
|
bool validCanvas = false;
|
||||||
|
bool hovered = false;
|
||||||
|
bool windowFocused = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IXCUIPanelCanvasHost {
|
||||||
|
public:
|
||||||
|
virtual ~IXCUIPanelCanvasHost() = default;
|
||||||
|
|
||||||
|
virtual XCUIPanelCanvasSession BeginCanvas(const XCUIPanelCanvasRequest& request) = 0;
|
||||||
|
virtual void DrawFilledRect(
|
||||||
|
const ::XCEngine::UI::UIRect& rect,
|
||||||
|
const ::XCEngine::UI::UIColor& color,
|
||||||
|
float rounding = 0.0f) = 0;
|
||||||
|
virtual void DrawOutlineRect(
|
||||||
|
const ::XCEngine::UI::UIRect& rect,
|
||||||
|
const ::XCEngine::UI::UIColor& color,
|
||||||
|
float thickness = 1.0f,
|
||||||
|
float rounding = 0.0f) = 0;
|
||||||
|
virtual void DrawText(
|
||||||
|
const ::XCEngine::UI::UIPoint& position,
|
||||||
|
std::string_view text,
|
||||||
|
const ::XCEngine::UI::UIColor& color,
|
||||||
|
float fontSize = 0.0f) = 0;
|
||||||
|
virtual void EndCanvas() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IXCUIPanelCanvasHost> CreateImGuiXCUIPanelCanvasHost();
|
||||||
|
|
||||||
|
} // namespace XCUIBackend
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "XCUIDemoPanel.h"
|
#include "XCUIDemoPanel.h"
|
||||||
|
|
||||||
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
||||||
|
#include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h"
|
||||||
#include "XCUIBackend/ImGuiXCUIInputAdapter.h"
|
#include "XCUIBackend/ImGuiXCUIInputAdapter.h"
|
||||||
|
|
||||||
#include <XCEngine/UI/Types.h>
|
#include <XCEngine/UI/Types.h>
|
||||||
@@ -20,27 +21,9 @@ namespace {
|
|||||||
|
|
||||||
constexpr float kCanvasHudHeight = 82.0f;
|
constexpr float kCanvasHudHeight = 82.0f;
|
||||||
constexpr float kCanvasHudPadding = 10.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 kPreviewDebugName[] = "XCUI Demo";
|
||||||
constexpr char kPreviewDebugSource[] = "new_editor.panels.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) {
|
bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
|
||||||
return point.x >= rect.x &&
|
return point.x >= rect.x &&
|
||||||
point.y >= rect.y &&
|
point.y >= rect.y &&
|
||||||
@@ -48,47 +31,13 @@ bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
|
|||||||
point.y <= rect.y + rect.height;
|
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(
|
void DrawRectOverlay(
|
||||||
ImDrawList* drawList,
|
::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost& canvasHost,
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIDemoRuntime& runtime,
|
::XCEngine::Editor::XCUIBackend::XCUIDemoRuntime& runtime,
|
||||||
const std::string& elementId,
|
const std::string& elementId,
|
||||||
ImU32 color,
|
const UI::UIColor& color,
|
||||||
const char* label) {
|
const char* label) {
|
||||||
if (drawList == nullptr || elementId.empty()) {
|
if (elementId.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,11 +46,12 @@ void DrawRectOverlay(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImVec2 minPoint(rect.x, rect.y);
|
canvasHost.DrawOutlineRect(rect, color, 2.0f, 6.0f);
|
||||||
const ImVec2 maxPoint(rect.x + rect.width, rect.y + rect.height);
|
|
||||||
drawList->AddRect(minPoint, maxPoint, color, 6.0f, 0, 2.0f);
|
|
||||||
if (label != nullptr && label[0] != '\0') {
|
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)
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter)
|
||||||
: Panel("XCUI Demo")
|
: Panel("XCUI Demo")
|
||||||
, m_inputSource(inputSource)
|
, m_inputSource(inputSource)
|
||||||
, m_previewPresenter(std::move(previewPresenter)) {
|
, m_previewPresenter(std::move(previewPresenter))
|
||||||
|
, m_canvasHost(::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()) {
|
||||||
if (m_previewPresenter == nullptr) {
|
if (m_previewPresenter == nullptr) {
|
||||||
m_previewPresenter = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter();
|
m_previewPresenter = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter();
|
||||||
}
|
}
|
||||||
@@ -168,6 +119,14 @@ void XCUIDemoPanel::SetHostedPreviewPresenter(
|
|||||||
m_lastPreviewStats = {};
|
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 {
|
bool XCUIDemoPanel::IsUsingNativeHostedPreview() const {
|
||||||
return m_previewPresenter != nullptr && m_previewPresenter->IsNativeQueued();
|
return m_previewPresenter != nullptr && m_previewPresenter->IsNativeQueued();
|
||||||
}
|
}
|
||||||
@@ -200,15 +159,10 @@ void XCUIDemoPanel::Render() {
|
|||||||
const ImVec2 hostRegion = ImGui::GetContentRegionAvail();
|
const ImVec2 hostRegion = ImGui::GetContentRegionAvail();
|
||||||
const float canvasHeight = (std::max)(140.0f, hostRegion.y - diagnosticsHeight);
|
const float canvasHeight = (std::max)(140.0f, hostRegion.y - diagnosticsHeight);
|
||||||
|
|
||||||
ImGui::BeginChild("XCUIDemoCanvasHost", ImVec2(0.0f, canvasHeight), true, ImGuiWindowFlags_NoScrollWithMouse);
|
if (m_canvasHost == nullptr) {
|
||||||
const ImVec2 canvasHostMin = ImGui::GetCursorScreenPos();
|
m_canvasHost = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost();
|
||||||
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;
|
|
||||||
const bool nativeHostedPreview = IsUsingNativeHostedPreview();
|
const bool nativeHostedPreview = IsUsingNativeHostedPreview();
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
|
||||||
const bool hasHostedSurfaceDescriptor =
|
const bool hasHostedSurfaceDescriptor =
|
||||||
@@ -217,38 +171,27 @@ void XCUIDemoPanel::Render() {
|
|||||||
m_previewPresenter->TryGetSurfaceDescriptor(kPreviewDebugName, hostedSurfaceDescriptor);
|
m_previewPresenter->TryGetSurfaceDescriptor(kPreviewDebugName, hostedSurfaceDescriptor);
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
|
||||||
const bool showHostedSurfaceImage =
|
const bool showHostedSurfaceImage =
|
||||||
validCanvas &&
|
|
||||||
nativeHostedPreview &&
|
nativeHostedPreview &&
|
||||||
m_previewPresenter != nullptr &&
|
m_previewPresenter != nullptr &&
|
||||||
m_previewPresenter->TryGetSurfaceImage(kPreviewDebugName, hostedSurfaceImage);
|
m_previewPresenter->TryGetSurfaceImage(kPreviewDebugName, hostedSurfaceImage);
|
||||||
const char* const previewPathLabel = GetPreviewPathLabel(nativeHostedPreview);
|
const char* const previewPathLabel = GetPreviewPathLabel(nativeHostedPreview);
|
||||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
const float topInset = m_showCanvasHud ? (kCanvasHudHeight + kCanvasHudPadding) : 0.0f;
|
||||||
|
::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasRequest canvasRequest = {};
|
||||||
if (validCanvas) {
|
canvasRequest.childId = "XCUIDemoCanvasHost";
|
||||||
ImGui::SetCursorScreenPos(canvasMin);
|
canvasRequest.height = canvasHeight;
|
||||||
if (showHostedSurfaceImage) {
|
canvasRequest.topInset = topInset;
|
||||||
ImGui::Image(
|
canvasRequest.showSurfaceImage = showHostedSurfaceImage;
|
||||||
ToImTextureId(hostedSurfaceImage.texture),
|
canvasRequest.surfaceImage = hostedSurfaceImage;
|
||||||
canvasSize,
|
canvasRequest.placeholderTitle =
|
||||||
ToImVec2(hostedSurfaceImage.uvMin),
|
nativeHostedPreview ? "Native XCUI preview pending" : "Legacy XCUI canvas host";
|
||||||
ToImVec2(hostedSurfaceImage.uvMax));
|
canvasRequest.placeholderSubtitle =
|
||||||
DrawHostedPreviewFrame(drawList, canvasMin, canvasSize);
|
nativeHostedPreview
|
||||||
} else {
|
? "Waiting for native queued render output to publish back into the sandbox panel."
|
||||||
ImGui::InvisibleButton("##XCUIDemoCanvas", canvasSize);
|
: "Legacy ImGui transition path stays active until native offscreen preview is enabled.";
|
||||||
const char* placeholderSubtitle =
|
const ::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession canvasSession =
|
||||||
nativeHostedPreview
|
m_canvasHost->BeginCanvas(canvasRequest);
|
||||||
? "Waiting for native queued render output to publish back into the sandbox panel."
|
const UI::UIRect canvasRect = canvasSession.canvasRect;
|
||||||
: "Legacy ImGui transition path stays active until native offscreen preview is enabled.";
|
const bool validCanvas = canvasSession.validCanvas;
|
||||||
DrawHostedPreviewPlaceholder(
|
|
||||||
drawList,
|
|
||||||
canvasMin,
|
|
||||||
canvasSize,
|
|
||||||
nativeHostedPreview ? "Native XCUI preview pending" : "Legacy XCUI canvas host",
|
|
||||||
placeholderSubtitle);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIDemoInputState input = {};
|
::XCEngine::Editor::XCUIBackend::XCUIDemoInputState input = {};
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions bridgeOptions = {};
|
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions bridgeOptions = {};
|
||||||
@@ -256,22 +199,22 @@ void XCUIDemoPanel::Render() {
|
|||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
std::chrono::steady_clock::now().time_since_epoch())
|
std::chrono::steady_clock::now().time_since_epoch())
|
||||||
.count());
|
.count());
|
||||||
const UI::UIRect canvasRect = ToUIRect(canvasMin, canvasSize);
|
|
||||||
|
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot snapshot = {};
|
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot snapshot = {};
|
||||||
if (m_inputSource != nullptr) {
|
if (m_inputSource != nullptr) {
|
||||||
bridgeOptions.hasPointerInsideOverride = true;
|
bridgeOptions.hasPointerInsideOverride = true;
|
||||||
bridgeOptions.windowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
bridgeOptions.windowFocused = canvasSession.windowFocused;
|
||||||
const UI::UIPoint pointerPosition = m_inputSource->GetPointerPosition();
|
const UI::UIPoint pointerPosition = m_inputSource->GetPointerPosition();
|
||||||
bridgeOptions.pointerInsideOverride = validCanvas && ContainsPoint(canvasRect, pointerPosition);
|
bridgeOptions.pointerInsideOverride = validCanvas && ContainsPoint(canvasRect, pointerPosition);
|
||||||
snapshot = m_inputSource->CaptureSnapshot(bridgeOptions);
|
snapshot = m_inputSource->CaptureSnapshot(bridgeOptions);
|
||||||
} else {
|
} else {
|
||||||
bridgeOptions.hasPointerInsideOverride = true;
|
bridgeOptions.hasPointerInsideOverride = true;
|
||||||
bridgeOptions.pointerInsideOverride = validCanvas && ImGui::IsItemHovered();
|
bridgeOptions.pointerInsideOverride = validCanvas && canvasSession.hovered;
|
||||||
bridgeOptions.windowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
bridgeOptions.windowFocused = canvasSession.windowFocused;
|
||||||
snapshot = ::XCEngine::Editor::XCUIBackend::ImGuiXCUIInputAdapter::CaptureSnapshot(
|
snapshot = ::XCEngine::Editor::XCUIBackend::ImGuiXCUIInputAdapter::CaptureSnapshot(
|
||||||
ImGui::GetIO(),
|
ImGui::GetIO(),
|
||||||
bridgeOptions);
|
bridgeOptions);
|
||||||
|
snapshot.pointerPosition = canvasSession.pointerPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_inputBridge.HasBaseline()) {
|
if (!m_inputBridge.HasBaseline()) {
|
||||||
@@ -315,48 +258,64 @@ void XCUIDemoPanel::Render() {
|
|||||||
hasHostedSurfaceDescriptor,
|
hasHostedSurfaceDescriptor,
|
||||||
showHostedSurfaceImage);
|
showHostedSurfaceImage);
|
||||||
if (m_showCanvasHud) {
|
if (m_showCanvasHud) {
|
||||||
const ImVec2 hudMin(canvasHostMin.x + 8.0f, canvasHostMin.y + 8.0f);
|
const float hudWidth = (std::min)(canvasSession.hostRect.width - 16.0f, 430.0f);
|
||||||
const ImVec2 hudMax(
|
const UI::UIRect hudRect(
|
||||||
canvasHostMin.x + (std::min)(availableSize.x - 8.0f, 430.0f),
|
canvasSession.hostRect.x + 8.0f,
|
||||||
canvasHostMin.y + kCanvasHudHeight);
|
canvasSession.hostRect.y + 8.0f,
|
||||||
drawList->AddRectFilled(
|
(std::max)(0.0f, hudWidth),
|
||||||
hudMin,
|
kCanvasHudHeight - 8.0f);
|
||||||
hudMax,
|
m_canvasHost->DrawFilledRect(
|
||||||
IM_COL32(16, 22, 30, 220),
|
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);
|
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));
|
const UI::UIPoint lineOrigin(hudRect.x + 10.0f, hudRect.y + 8.0f);
|
||||||
ImGui::BeginGroup();
|
const UI::UIColor textColor(191.0f / 255.0f, 205.0f / 255.0f, 224.0f / 255.0f, 1.0f);
|
||||||
ImGui::TextUnformatted("XCUI Runtime");
|
m_canvasHost->DrawText(lineOrigin, "XCUI Runtime", UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||||
ImGui::Text("%s | %s", previewPathLabel, previewStateLabel);
|
|
||||||
ImGui::TextUnformatted(stats.statusMessage.c_str());
|
std::string previewLine = std::string(previewPathLabel) + " | " + previewStateLabel;
|
||||||
ImGui::Text(
|
std::string treeLine =
|
||||||
"Tree %llu | Elements %zu | Commands %zu",
|
"Tree " + std::to_string(static_cast<unsigned long long>(stats.treeGeneration)) +
|
||||||
static_cast<unsigned long long>(stats.treeGeneration),
|
" | Elements " + std::to_string(stats.elementCount) +
|
||||||
stats.elementCount,
|
" | Commands " + std::to_string(stats.commandCount);
|
||||||
stats.commandCount);
|
std::string flushLine =
|
||||||
ImGui::Text(
|
"Submit " + std::to_string(m_lastPreviewStats.submittedDrawListCount) +
|
||||||
"Submit %zu/%zu | Flush %zu/%zu",
|
"/" + std::to_string(m_lastPreviewStats.submittedCommandCount) +
|
||||||
m_lastPreviewStats.submittedDrawListCount,
|
" | Flush " + std::to_string(m_lastPreviewStats.flushedDrawListCount) +
|
||||||
m_lastPreviewStats.submittedCommandCount,
|
"/" + std::to_string(m_lastPreviewStats.flushedCommandCount);
|
||||||
m_lastPreviewStats.flushedDrawListCount,
|
|
||||||
m_lastPreviewStats.flushedCommandCount);
|
m_canvasHost->DrawText(UI::UIPoint(lineOrigin.x, lineOrigin.y + 18.0f), previewLine, textColor);
|
||||||
ImGui::EndGroup();
|
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)) {
|
if (m_showDebugRects && validCanvas && (!nativeHostedPreview || showHostedSurfaceImage)) {
|
||||||
DrawRectOverlay(drawList, m_runtime, stats.hoveredElementId, IM_COL32(255, 195, 64, 255), "hover");
|
DrawRectOverlay(
|
||||||
DrawRectOverlay(drawList, m_runtime, stats.focusedElementId, IM_COL32(64, 214, 255, 255), "focus");
|
*m_canvasHost,
|
||||||
DrawRectOverlay(drawList, m_runtime, "toggleAccent", IM_COL32(150, 255, 150, 160), "toggle");
|
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::Separator();
|
||||||
ImGui::BeginChild("XCUIDemoDiagnostics", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_NoScrollbar);
|
ImGui::BeginChild("XCUIDemoDiagnostics", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_NoScrollbar);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
||||||
#include "XCUIBackend/XCUIInputBridge.h"
|
#include "XCUIBackend/XCUIInputBridge.h"
|
||||||
|
#include "XCUIBackend/XCUIPanelCanvasHost.h"
|
||||||
#include "XCUIBackend/XCUIDemoRuntime.h"
|
#include "XCUIBackend/XCUIDemoRuntime.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -24,6 +25,8 @@ public:
|
|||||||
void SetHostedPreviewEnabled(bool enabled);
|
void SetHostedPreviewEnabled(bool enabled);
|
||||||
void SetHostedPreviewPresenter(
|
void SetHostedPreviewPresenter(
|
||||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter);
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter);
|
||||||
|
void SetCanvasHost(
|
||||||
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost> canvasHost);
|
||||||
bool IsHostedPreviewEnabled() const { return m_hostedPreviewEnabled; }
|
bool IsHostedPreviewEnabled() const { return m_hostedPreviewEnabled; }
|
||||||
bool IsUsingNativeHostedPreview() const;
|
bool IsUsingNativeHostedPreview() const;
|
||||||
const ::XCEngine::Editor::XCUIBackend::XCUIDemoFrameResult& GetFrameResult() const;
|
const ::XCEngine::Editor::XCUIBackend::XCUIDemoFrameResult& GetFrameResult() const;
|
||||||
@@ -38,6 +41,7 @@ private:
|
|||||||
::XCEngine::Editor::XCUIBackend::XCUIInputBridge m_inputBridge;
|
::XCEngine::Editor::XCUIBackend::XCUIInputBridge m_inputBridge;
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIDemoRuntime m_runtime;
|
::XCEngine::Editor::XCUIBackend::XCUIDemoRuntime m_runtime;
|
||||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> m_previewPresenter;
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> m_previewPresenter;
|
||||||
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost> m_canvasHost;
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats m_lastPreviewStats = {};
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats m_lastPreviewStats = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "XCUILayoutLabPanel.h"
|
#include "XCUILayoutLabPanel.h"
|
||||||
|
|
||||||
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
||||||
|
#include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h"
|
||||||
#include <XCEngine/UI/Types.h>
|
#include <XCEngine/UI/Types.h>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
@@ -13,27 +14,9 @@ namespace NewEditor {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
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 Layout Lab";
|
constexpr char kPreviewDebugName[] = "XCUI Layout Lab";
|
||||||
constexpr char kPreviewDebugSource[] = "new_editor.panels.xcui_layout_lab";
|
constexpr char kPreviewDebugSource[] = "new_editor.panels.xcui_layout_lab";
|
||||||
|
|
||||||
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) {
|
bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
|
||||||
return point.x >= rect.x &&
|
return point.x >= rect.x &&
|
||||||
point.y >= rect.y &&
|
point.y >= rect.y &&
|
||||||
@@ -41,60 +24,6 @@ bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
|
|||||||
point.y <= rect.y + rect.height;
|
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 DrawCanvasBadge(
|
|
||||||
ImDrawList* drawList,
|
|
||||||
const ImVec2& minPoint,
|
|
||||||
const char* title,
|
|
||||||
const char* subtitle) {
|
|
||||||
if (drawList == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ImVec2 badgeMin(minPoint.x + 10.0f, minPoint.y + 10.0f);
|
|
||||||
const ImVec2 badgeMax(minPoint.x + 300.0f, minPoint.y + 52.0f);
|
|
||||||
drawList->AddRectFilled(badgeMin, badgeMax, IM_COL32(16, 22, 30, 216), 8.0f);
|
|
||||||
drawList->AddRect(badgeMin, badgeMax, IM_COL32(52, 72, 96, 255), 8.0f, 0, 1.0f);
|
|
||||||
drawList->AddText(ImVec2(badgeMin.x + 10.0f, badgeMin.y + 8.0f), kPreviewPlaceholderText, title);
|
|
||||||
drawList->AddText(
|
|
||||||
ImVec2(badgeMin.x + 10.0f, badgeMin.y + 28.0f),
|
|
||||||
kPreviewPlaceholderSubtleText,
|
|
||||||
subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* GetPreviewPathLabel(bool nativeHostedPreview) {
|
const char* GetPreviewPathLabel(bool nativeHostedPreview) {
|
||||||
return nativeHostedPreview ? "native queued offscreen surface" : "legacy imgui transition";
|
return nativeHostedPreview ? "native queued offscreen surface" : "legacy imgui transition";
|
||||||
}
|
}
|
||||||
@@ -127,7 +56,8 @@ XCUILayoutLabPanel::XCUILayoutLabPanel(
|
|||||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter)
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter)
|
||||||
: Panel("XCUI Layout Lab")
|
: Panel("XCUI Layout Lab")
|
||||||
, m_inputSource(inputSource)
|
, m_inputSource(inputSource)
|
||||||
, m_previewPresenter(std::move(previewPresenter)) {
|
, m_previewPresenter(std::move(previewPresenter))
|
||||||
|
, m_canvasHost(::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()) {
|
||||||
if (m_previewPresenter == nullptr) {
|
if (m_previewPresenter == nullptr) {
|
||||||
m_previewPresenter = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter();
|
m_previewPresenter = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter();
|
||||||
}
|
}
|
||||||
@@ -158,6 +88,14 @@ void XCUILayoutLabPanel::SetHostedPreviewPresenter(
|
|||||||
m_lastPreviewStats = {};
|
m_lastPreviewStats = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XCUILayoutLabPanel::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 XCUILayoutLabPanel::IsUsingNativeHostedPreview() const {
|
bool XCUILayoutLabPanel::IsUsingNativeHostedPreview() const {
|
||||||
return m_previewPresenter != nullptr && m_previewPresenter->IsNativeQueued();
|
return m_previewPresenter != nullptr && m_previewPresenter->IsNativeQueued();
|
||||||
}
|
}
|
||||||
@@ -186,10 +124,10 @@ void XCUILayoutLabPanel::Render() {
|
|||||||
const ImVec2 hostRegion = ImGui::GetContentRegionAvail();
|
const ImVec2 hostRegion = ImGui::GetContentRegionAvail();
|
||||||
const float canvasHeight = (std::max)(140.0f, hostRegion.y - diagnosticsHeight);
|
const float canvasHeight = (std::max)(140.0f, hostRegion.y - diagnosticsHeight);
|
||||||
|
|
||||||
ImGui::BeginChild("XCUILayoutLabCanvasHost", ImVec2(0.0f, canvasHeight), true, ImGuiWindowFlags_NoScrollWithMouse);
|
if (m_canvasHost == nullptr) {
|
||||||
const ImVec2 canvasMin = ImGui::GetCursorScreenPos();
|
m_canvasHost = ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost();
|
||||||
const ImVec2 canvasSize = ImGui::GetContentRegionAvail();
|
}
|
||||||
const bool validCanvas = canvasSize.x > 1.0f && canvasSize.y > 1.0f;
|
|
||||||
const bool nativeHostedPreview = IsUsingNativeHostedPreview();
|
const bool nativeHostedPreview = IsUsingNativeHostedPreview();
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
|
||||||
const bool hasHostedSurfaceDescriptor =
|
const bool hasHostedSurfaceDescriptor =
|
||||||
@@ -198,48 +136,36 @@ void XCUILayoutLabPanel::Render() {
|
|||||||
m_previewPresenter->TryGetSurfaceDescriptor(kPreviewDebugName, hostedSurfaceDescriptor);
|
m_previewPresenter->TryGetSurfaceDescriptor(kPreviewDebugName, hostedSurfaceDescriptor);
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
|
||||||
const bool showHostedSurfaceImage =
|
const bool showHostedSurfaceImage =
|
||||||
validCanvas &&
|
|
||||||
nativeHostedPreview &&
|
nativeHostedPreview &&
|
||||||
m_previewPresenter != nullptr &&
|
m_previewPresenter != nullptr &&
|
||||||
m_previewPresenter->TryGetSurfaceImage(kPreviewDebugName, hostedSurfaceImage);
|
m_previewPresenter->TryGetSurfaceImage(kPreviewDebugName, hostedSurfaceImage);
|
||||||
const char* const previewPathLabel = GetPreviewPathLabel(nativeHostedPreview);
|
const char* const previewPathLabel = GetPreviewPathLabel(nativeHostedPreview);
|
||||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasRequest canvasRequest = {};
|
||||||
|
canvasRequest.childId = "XCUILayoutLabCanvasHost";
|
||||||
if (validCanvas) {
|
canvasRequest.height = canvasHeight;
|
||||||
ImGui::SetCursorScreenPos(canvasMin);
|
canvasRequest.showSurfaceImage = showHostedSurfaceImage;
|
||||||
if (showHostedSurfaceImage) {
|
canvasRequest.surfaceImage = hostedSurfaceImage;
|
||||||
ImGui::Image(
|
canvasRequest.placeholderTitle =
|
||||||
ToImTextureId(hostedSurfaceImage.texture),
|
nativeHostedPreview ? "Native layout preview pending" : "Legacy layout canvas host";
|
||||||
canvasSize,
|
canvasRequest.placeholderSubtitle =
|
||||||
ToImVec2(hostedSurfaceImage.uvMin),
|
nativeHostedPreview
|
||||||
ToImVec2(hostedSurfaceImage.uvMax));
|
? "Waiting for native queued render output to publish back into the layout sandbox."
|
||||||
DrawHostedPreviewFrame(drawList, canvasMin, canvasSize);
|
: "Legacy ImGui transition path remains active until native offscreen preview is enabled.";
|
||||||
} else {
|
canvasRequest.badgeTitle = "Layout Lab";
|
||||||
ImGui::InvisibleButton("##XCUILayoutLabCanvas", canvasSize);
|
canvasRequest.badgeSubtitle = previewPathLabel;
|
||||||
const char* placeholderSubtitle =
|
const ::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession canvasSession =
|
||||||
nativeHostedPreview
|
m_canvasHost->BeginCanvas(canvasRequest);
|
||||||
? "Waiting for native queued render output to publish back into the layout sandbox."
|
const UI::UIRect canvasRect = canvasSession.canvasRect;
|
||||||
: "Legacy ImGui transition path remains active until native offscreen preview is enabled.";
|
const bool validCanvas = canvasSession.validCanvas;
|
||||||
DrawHostedPreviewPlaceholder(
|
|
||||||
drawList,
|
|
||||||
canvasMin,
|
|
||||||
canvasSize,
|
|
||||||
nativeHostedPreview ? "Native layout preview pending" : "Legacy layout canvas host",
|
|
||||||
placeholderSubtitle);
|
|
||||||
}
|
|
||||||
DrawCanvasBadge(drawList, canvasMin, "Layout Lab", previewPathLabel);
|
|
||||||
} else {
|
|
||||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
::XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = {};
|
::XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState input = {};
|
||||||
input.canvasRect = ToUIRect(canvasMin, canvasSize);
|
input.canvasRect = canvasRect;
|
||||||
if (m_inputSource != nullptr) {
|
if (m_inputSource != nullptr) {
|
||||||
input.pointerPosition = m_inputSource->GetPointerPosition();
|
input.pointerPosition = m_inputSource->GetPointerPosition();
|
||||||
input.pointerInside = validCanvas && ContainsPoint(input.canvasRect, input.pointerPosition);
|
input.pointerInside = validCanvas && ContainsPoint(input.canvasRect, input.pointerPosition);
|
||||||
} else {
|
} else {
|
||||||
input.pointerPosition = UI::UIPoint(ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y);
|
input.pointerPosition = canvasSession.pointerPosition;
|
||||||
input.pointerInside = validCanvas && ImGui::IsItemHovered();
|
input.pointerInside = validCanvas && canvasSession.hovered;
|
||||||
}
|
}
|
||||||
input.pointerPressed = input.pointerInside && ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
input.pointerPressed = input.pointerInside && ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
|
|
||||||
@@ -264,7 +190,7 @@ void XCUILayoutLabPanel::Render() {
|
|||||||
m_lastPreviewStats,
|
m_lastPreviewStats,
|
||||||
hasHostedSurfaceDescriptor,
|
hasHostedSurfaceDescriptor,
|
||||||
showHostedSurfaceImage);
|
showHostedSurfaceImage);
|
||||||
ImGui::EndChild();
|
m_canvasHost->EndCanvas();
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::BeginChild("XCUILayoutLabDiagnostics", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_NoScrollbar);
|
ImGui::BeginChild("XCUILayoutLabDiagnostics", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_NoScrollbar);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
|
||||||
#include "XCUIBackend/XCUIInputBridge.h"
|
#include "XCUIBackend/XCUIInputBridge.h"
|
||||||
|
#include "XCUIBackend/XCUIPanelCanvasHost.h"
|
||||||
#include "XCUIBackend/XCUILayoutLabRuntime.h"
|
#include "XCUIBackend/XCUILayoutLabRuntime.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -24,6 +25,8 @@ public:
|
|||||||
void SetHostedPreviewEnabled(bool enabled);
|
void SetHostedPreviewEnabled(bool enabled);
|
||||||
void SetHostedPreviewPresenter(
|
void SetHostedPreviewPresenter(
|
||||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter);
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> previewPresenter);
|
||||||
|
void SetCanvasHost(
|
||||||
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost> canvasHost);
|
||||||
bool IsHostedPreviewEnabled() const { return m_hostedPreviewEnabled; }
|
bool IsHostedPreviewEnabled() const { return m_hostedPreviewEnabled; }
|
||||||
bool IsUsingNativeHostedPreview() const;
|
bool IsUsingNativeHostedPreview() const;
|
||||||
const ::XCEngine::Editor::XCUIBackend::XCUILayoutLabFrameResult& GetFrameResult() const;
|
const ::XCEngine::Editor::XCUIBackend::XCUILayoutLabFrameResult& GetFrameResult() const;
|
||||||
@@ -35,6 +38,7 @@ private:
|
|||||||
::XCEngine::Editor::XCUIBackend::XCUIWin32InputSource* m_inputSource = nullptr;
|
::XCEngine::Editor::XCUIBackend::XCUIWin32InputSource* m_inputSource = nullptr;
|
||||||
::XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime m_runtime;
|
::XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime m_runtime;
|
||||||
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> m_previewPresenter;
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> m_previewPresenter;
|
||||||
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost> m_canvasHost;
|
||||||
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats m_lastPreviewStats = {};
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats m_lastPreviewStats = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user