Build XCUI splitter foundation and test harness
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
|
||||
class NativeRenderer;
|
||||
|
||||
class AutoScreenshotController {
|
||||
public:
|
||||
void Initialize(const std::filesystem::path& captureRoot);
|
||||
void Shutdown();
|
||||
|
||||
void RequestCapture(std::string reason);
|
||||
void CaptureIfRequested(
|
||||
NativeRenderer& renderer,
|
||||
const ::XCEngine::UI::UIDrawData& drawData,
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
bool framePresented);
|
||||
|
||||
bool HasPendingCapture() const;
|
||||
const std::filesystem::path& GetLatestCapturePath() const;
|
||||
const std::string& GetLastCaptureSummary() const;
|
||||
const std::string& GetLastCaptureError() const;
|
||||
|
||||
private:
|
||||
std::filesystem::path BuildHistoryCapturePath(std::string_view reason) const;
|
||||
|
||||
static std::string BuildTimestampString();
|
||||
static std::string SanitizeReason(std::string_view reason);
|
||||
|
||||
std::filesystem::path m_captureRoot = {};
|
||||
std::filesystem::path m_historyRoot = {};
|
||||
std::filesystem::path m_latestCapturePath = {};
|
||||
std::string m_pendingReason = {};
|
||||
std::string m_lastCaptureSummary = {};
|
||||
std::string m_lastCaptureError = {};
|
||||
std::uint64_t m_captureCount = 0;
|
||||
bool m_capturePending = false;
|
||||
};
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "Application.h"
|
||||
|
||||
#include "SandboxFrameBuilder.h"
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -15,8 +15,7 @@
|
||||
#define XCNEWEDITOR_REPO_ROOT "."
|
||||
#endif
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
namespace XCEngine::NewEditor {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -25,14 +24,14 @@ using ::XCEngine::UI::UIDrawData;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
using ::XCEngine::UI::UIInputModifiers;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIPointerButton;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
using ::XCEngine::UI::Runtime::UIScreenFrameInput;
|
||||
using ::XCEngine::Input::KeyCode;
|
||||
|
||||
constexpr const wchar_t* kWindowClassName = L"XCNewEditorNativeSandbox";
|
||||
constexpr const wchar_t* kWindowTitle = L"XCNewEditor Native Sandbox";
|
||||
constexpr const wchar_t* kWindowClassName = L"XCNewEditorShellHost";
|
||||
constexpr const wchar_t* kWindowTitle = L"XCUI New Editor";
|
||||
constexpr auto kReloadPollInterval = std::chrono::milliseconds(150);
|
||||
|
||||
constexpr UIColor kOverlayBgColor(0.10f, 0.10f, 0.10f, 0.95f);
|
||||
@@ -83,21 +82,79 @@ std::string FormatPoint(const UIPoint& point) {
|
||||
return "(" + FormatFloat(point.x) + ", " + FormatFloat(point.y) + ")";
|
||||
}
|
||||
|
||||
std::string FormatRect(const UIRect& rect) {
|
||||
return "(" + FormatFloat(rect.x) +
|
||||
", " + FormatFloat(rect.y) +
|
||||
", " + FormatFloat(rect.width) +
|
||||
", " + FormatFloat(rect.height) +
|
||||
")";
|
||||
std::int32_t MapVirtualKeyToUIKeyCode(WPARAM wParam) {
|
||||
switch (wParam) {
|
||||
case 'A': return static_cast<std::int32_t>(KeyCode::A);
|
||||
case 'B': return static_cast<std::int32_t>(KeyCode::B);
|
||||
case 'C': return static_cast<std::int32_t>(KeyCode::C);
|
||||
case 'D': return static_cast<std::int32_t>(KeyCode::D);
|
||||
case 'E': return static_cast<std::int32_t>(KeyCode::E);
|
||||
case 'F': return static_cast<std::int32_t>(KeyCode::F);
|
||||
case 'G': return static_cast<std::int32_t>(KeyCode::G);
|
||||
case 'H': return static_cast<std::int32_t>(KeyCode::H);
|
||||
case 'I': return static_cast<std::int32_t>(KeyCode::I);
|
||||
case 'J': return static_cast<std::int32_t>(KeyCode::J);
|
||||
case 'K': return static_cast<std::int32_t>(KeyCode::K);
|
||||
case 'L': return static_cast<std::int32_t>(KeyCode::L);
|
||||
case 'M': return static_cast<std::int32_t>(KeyCode::M);
|
||||
case 'N': return static_cast<std::int32_t>(KeyCode::N);
|
||||
case 'O': return static_cast<std::int32_t>(KeyCode::O);
|
||||
case 'P': return static_cast<std::int32_t>(KeyCode::P);
|
||||
case 'Q': return static_cast<std::int32_t>(KeyCode::Q);
|
||||
case 'R': return static_cast<std::int32_t>(KeyCode::R);
|
||||
case 'S': return static_cast<std::int32_t>(KeyCode::S);
|
||||
case 'T': return static_cast<std::int32_t>(KeyCode::T);
|
||||
case 'U': return static_cast<std::int32_t>(KeyCode::U);
|
||||
case 'V': return static_cast<std::int32_t>(KeyCode::V);
|
||||
case 'W': return static_cast<std::int32_t>(KeyCode::W);
|
||||
case 'X': return static_cast<std::int32_t>(KeyCode::X);
|
||||
case 'Y': return static_cast<std::int32_t>(KeyCode::Y);
|
||||
case 'Z': return static_cast<std::int32_t>(KeyCode::Z);
|
||||
case '0': return static_cast<std::int32_t>(KeyCode::Zero);
|
||||
case '1': return static_cast<std::int32_t>(KeyCode::One);
|
||||
case '2': return static_cast<std::int32_t>(KeyCode::Two);
|
||||
case '3': return static_cast<std::int32_t>(KeyCode::Three);
|
||||
case '4': return static_cast<std::int32_t>(KeyCode::Four);
|
||||
case '5': return static_cast<std::int32_t>(KeyCode::Five);
|
||||
case '6': return static_cast<std::int32_t>(KeyCode::Six);
|
||||
case '7': return static_cast<std::int32_t>(KeyCode::Seven);
|
||||
case '8': return static_cast<std::int32_t>(KeyCode::Eight);
|
||||
case '9': return static_cast<std::int32_t>(KeyCode::Nine);
|
||||
case VK_SPACE: return static_cast<std::int32_t>(KeyCode::Space);
|
||||
case VK_TAB: return static_cast<std::int32_t>(KeyCode::Tab);
|
||||
case VK_RETURN: return static_cast<std::int32_t>(KeyCode::Enter);
|
||||
case VK_ESCAPE: return static_cast<std::int32_t>(KeyCode::Escape);
|
||||
case VK_SHIFT: return static_cast<std::int32_t>(KeyCode::LeftShift);
|
||||
case VK_CONTROL: return static_cast<std::int32_t>(KeyCode::LeftCtrl);
|
||||
case VK_MENU: return static_cast<std::int32_t>(KeyCode::LeftAlt);
|
||||
case VK_UP: return static_cast<std::int32_t>(KeyCode::Up);
|
||||
case VK_DOWN: return static_cast<std::int32_t>(KeyCode::Down);
|
||||
case VK_LEFT: return static_cast<std::int32_t>(KeyCode::Left);
|
||||
case VK_RIGHT: return static_cast<std::int32_t>(KeyCode::Right);
|
||||
case VK_HOME: return static_cast<std::int32_t>(KeyCode::Home);
|
||||
case VK_END: return static_cast<std::int32_t>(KeyCode::End);
|
||||
case VK_PRIOR: return static_cast<std::int32_t>(KeyCode::PageUp);
|
||||
case VK_NEXT: return static_cast<std::int32_t>(KeyCode::PageDown);
|
||||
case VK_DELETE: return static_cast<std::int32_t>(KeyCode::Delete);
|
||||
case VK_BACK: return static_cast<std::int32_t>(KeyCode::Backspace);
|
||||
case VK_F1: return static_cast<std::int32_t>(KeyCode::F1);
|
||||
case VK_F2: return static_cast<std::int32_t>(KeyCode::F2);
|
||||
case VK_F3: return static_cast<std::int32_t>(KeyCode::F3);
|
||||
case VK_F4: return static_cast<std::int32_t>(KeyCode::F4);
|
||||
case VK_F5: return static_cast<std::int32_t>(KeyCode::F5);
|
||||
case VK_F6: return static_cast<std::int32_t>(KeyCode::F6);
|
||||
case VK_F7: return static_cast<std::int32_t>(KeyCode::F7);
|
||||
case VK_F8: return static_cast<std::int32_t>(KeyCode::F8);
|
||||
case VK_F9: return static_cast<std::int32_t>(KeyCode::F9);
|
||||
case VK_F10: return static_cast<std::int32_t>(KeyCode::F10);
|
||||
case VK_F11: return static_cast<std::int32_t>(KeyCode::F11);
|
||||
case VK_F12: return static_cast<std::int32_t>(KeyCode::F12);
|
||||
default: return static_cast<std::int32_t>(KeyCode::None);
|
||||
}
|
||||
}
|
||||
|
||||
UIInputModifiers BuildInputModifiers(size_t wParam) {
|
||||
UIInputModifiers modifiers = {};
|
||||
modifiers.shift = (wParam & MK_SHIFT) != 0;
|
||||
modifiers.control = (wParam & MK_CONTROL) != 0;
|
||||
modifiers.alt = (GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
modifiers.super = (GetKeyState(VK_LWIN) & 0x8000) != 0 || (GetKeyState(VK_RWIN) & 0x8000) != 0;
|
||||
return modifiers;
|
||||
bool IsRepeatKeyMessage(LPARAM lParam) {
|
||||
return (static_cast<unsigned long>(lParam) & (1ul << 30)) != 0ul;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -130,6 +187,7 @@ int Application::Run(HINSTANCE hInstance, int nCmdShow) {
|
||||
|
||||
bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
m_hInstance = hInstance;
|
||||
m_shellAssetDefinition = BuildDefaultEditorShellAsset(ResolveRepoRootPath());
|
||||
|
||||
WNDCLASSEXW windowClass = {};
|
||||
windowClass.cbSize = sizeof(windowClass);
|
||||
@@ -170,7 +228,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
|
||||
m_startTime = std::chrono::steady_clock::now();
|
||||
m_lastFrameTime = m_startTime;
|
||||
m_autoScreenshot.Initialize(ResolveRepoRelativePath("new_editor/captures"));
|
||||
m_autoScreenshot.Initialize(m_shellAssetDefinition.captureRootPath);
|
||||
LoadStructuredScreen("startup");
|
||||
return true;
|
||||
}
|
||||
@@ -209,7 +267,6 @@ void Application::RenderFrame() {
|
||||
const float height = static_cast<float>((std::max)(clientRect.bottom - clientRect.top, 1L));
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const double timeSeconds = std::chrono::duration<double>(now - m_startTime).count();
|
||||
double deltaTimeSeconds = std::chrono::duration<double>(now - m_lastFrameTime).count();
|
||||
if (deltaTimeSeconds <= 0.0) {
|
||||
deltaTimeSeconds = 1.0 / 60.0;
|
||||
@@ -234,17 +291,10 @@ void Application::RenderFrame() {
|
||||
drawData.AddDrawList(drawList);
|
||||
}
|
||||
|
||||
m_runtimeStatus = "Authored XCUI";
|
||||
m_runtimeStatus = "XCUI Editor Shell";
|
||||
m_runtimeError = frame.errorMessage;
|
||||
}
|
||||
|
||||
if (drawData.Empty()) {
|
||||
SandboxFrameOptions options = {};
|
||||
options.width = width;
|
||||
options.height = height;
|
||||
options.timeSeconds = timeSeconds;
|
||||
drawData = BuildSandboxFrame(options);
|
||||
m_runtimeStatus = "Fallback Sandbox";
|
||||
} else {
|
||||
m_runtimeStatus = "Editor Shell | Load Error";
|
||||
if (m_runtimeError.empty() && !m_screenPlayer.IsLoaded()) {
|
||||
m_runtimeError = m_screenPlayer.GetLastError();
|
||||
}
|
||||
@@ -276,7 +326,7 @@ void Application::QueuePointerEvent(UIInputEventType type, UIPointerButton butto
|
||||
event.position = UIPoint(
|
||||
static_cast<float>(GET_X_LPARAM(lParam)),
|
||||
static_cast<float>(GET_Y_LPARAM(lParam)));
|
||||
event.modifiers = BuildInputModifiers(static_cast<size_t>(wParam));
|
||||
event.modifiers = m_inputModifierTracker.BuildPointerModifiers(static_cast<std::size_t>(wParam));
|
||||
m_pendingInputEvents.push_back(event);
|
||||
}
|
||||
|
||||
@@ -307,19 +357,43 @@ void Application::QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM
|
||||
event.type = UIInputEventType::PointerWheel;
|
||||
event.position = UIPoint(static_cast<float>(screenPoint.x), static_cast<float>(screenPoint.y));
|
||||
event.wheelDelta = static_cast<float>(wheelDelta);
|
||||
event.modifiers = BuildInputModifiers(static_cast<size_t>(wParam));
|
||||
event.modifiers = m_inputModifierTracker.BuildPointerModifiers(static_cast<std::size_t>(wParam));
|
||||
m_pendingInputEvents.push_back(event);
|
||||
}
|
||||
|
||||
void Application::QueueKeyEvent(UIInputEventType type, WPARAM wParam, LPARAM lParam) {
|
||||
UIInputEvent event = {};
|
||||
event.type = type;
|
||||
event.keyCode = MapVirtualKeyToUIKeyCode(wParam);
|
||||
event.modifiers = m_inputModifierTracker.ApplyKeyMessage(type, wParam, lParam);
|
||||
event.repeat = IsRepeatKeyMessage(lParam);
|
||||
m_pendingInputEvents.push_back(event);
|
||||
}
|
||||
|
||||
void Application::QueueCharacterEvent(WPARAM wParam, LPARAM) {
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::Character;
|
||||
event.character = static_cast<std::uint32_t>(wParam);
|
||||
event.modifiers = m_inputModifierTracker.GetCurrentModifiers();
|
||||
m_pendingInputEvents.push_back(event);
|
||||
}
|
||||
|
||||
void Application::QueueWindowFocusEvent(UIInputEventType type) {
|
||||
UIInputEvent event = {};
|
||||
event.type = type;
|
||||
m_pendingInputEvents.push_back(event);
|
||||
}
|
||||
|
||||
bool Application::LoadStructuredScreen(const char* triggerReason) {
|
||||
(void)triggerReason;
|
||||
m_screenAsset = {};
|
||||
m_screenAsset.screenId = "new_editor.editor_shell";
|
||||
m_screenAsset.documentPath = ResolveRepoRelativePath("new_editor/ui/views/editor_shell.xcui").string();
|
||||
m_screenAsset.themePath = ResolveRepoRelativePath("new_editor/ui/themes/editor_shell.xctheme").string();
|
||||
m_screenAsset.screenId = m_shellAssetDefinition.screenId;
|
||||
m_screenAsset.documentPath = m_shellAssetDefinition.documentPath.string();
|
||||
m_screenAsset.themePath = m_shellAssetDefinition.themePath.string();
|
||||
|
||||
const bool loaded = m_screenPlayer.Load(m_screenAsset);
|
||||
m_useStructuredScreen = loaded;
|
||||
m_runtimeStatus = loaded ? "Authored XCUI" : "Fallback Sandbox";
|
||||
m_runtimeStatus = loaded ? "XCUI Editor Shell" : "Editor Shell | Load Error";
|
||||
m_runtimeError = loaded ? std::string() : m_screenPlayer.GetLastError();
|
||||
RebuildTrackedFileStates();
|
||||
return loaded;
|
||||
@@ -404,12 +478,13 @@ bool Application::DetectTrackedFileChange() const {
|
||||
|
||||
void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float height) const {
|
||||
const bool authoredMode = m_useStructuredScreen && m_screenPlayer.IsLoaded();
|
||||
const float panelWidth = authoredMode ? 420.0f : 360.0f;
|
||||
const float panelWidth = authoredMode ? 430.0f : 380.0f;
|
||||
std::vector<std::string> detailLines = {};
|
||||
detailLines.push_back(
|
||||
authoredMode
|
||||
? "Hot reload watches authored UI resources."
|
||||
: "Using native fallback while authored UI is invalid.");
|
||||
? "Hot reload watches editor shell resources."
|
||||
: "Authored editor shell failed to load.");
|
||||
detailLines.push_back("Document: editor_shell.xcui");
|
||||
|
||||
if (authoredMode) {
|
||||
const auto& inputDebug = m_documentHost.GetInputDebugSnapshot();
|
||||
@@ -424,18 +499,24 @@ void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float
|
||||
" | " +
|
||||
ExtractStateKeyTail(inputDebug.captureStateKey));
|
||||
if (!inputDebug.lastEventType.empty()) {
|
||||
const std::string eventPosition = inputDebug.lastEventType == "KeyDown" ||
|
||||
inputDebug.lastEventType == "KeyUp" ||
|
||||
inputDebug.lastEventType == "Character" ||
|
||||
inputDebug.lastEventType == "FocusGained" ||
|
||||
inputDebug.lastEventType == "FocusLost"
|
||||
? std::string()
|
||||
: " at " + FormatPoint(inputDebug.pointerPosition);
|
||||
detailLines.push_back(
|
||||
"Last input: " +
|
||||
inputDebug.lastEventType +
|
||||
" at " +
|
||||
FormatPoint(inputDebug.pointerPosition));
|
||||
eventPosition);
|
||||
detailLines.push_back(
|
||||
"Route: " +
|
||||
inputDebug.lastTargetKind +
|
||||
" -> " +
|
||||
ExtractStateKeyTail(inputDebug.lastTargetStateKey));
|
||||
detailLines.push_back(
|
||||
"Result: " +
|
||||
"Last event result: " +
|
||||
(inputDebug.lastResult.empty() ? std::string("n/a") : inputDebug.lastResult));
|
||||
}
|
||||
}
|
||||
@@ -445,13 +526,15 @@ void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float
|
||||
} else if (!m_autoScreenshot.GetLastCaptureSummary().empty()) {
|
||||
detailLines.push_back(TruncateText(m_autoScreenshot.GetLastCaptureSummary(), 78u));
|
||||
} else {
|
||||
detailLines.push_back("Screenshots: manual only (F12)");
|
||||
detailLines.push_back("Screenshots: F12 -> new_editor/captures/");
|
||||
}
|
||||
|
||||
if (!m_runtimeError.empty()) {
|
||||
detailLines.push_back(TruncateText(m_runtimeError, 78u));
|
||||
} else if (!m_autoScreenshot.GetLastCaptureError().empty()) {
|
||||
detailLines.push_back(TruncateText(m_autoScreenshot.GetLastCaptureError(), 78u));
|
||||
} else if (!authoredMode) {
|
||||
detailLines.push_back("No fallback sandbox is rendered in this host.");
|
||||
}
|
||||
|
||||
const float panelHeight = 38.0f + static_cast<float>(detailLines.size()) * 18.0f;
|
||||
@@ -466,7 +549,7 @@ void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float
|
||||
4.0f);
|
||||
overlay.AddText(
|
||||
UIPoint(panelRect.x + 28.0f, panelRect.y + 10.0f),
|
||||
m_runtimeStatus.empty() ? "Runtime State" : m_runtimeStatus,
|
||||
m_runtimeStatus.empty() ? "XCUI Editor Shell" : m_runtimeStatus,
|
||||
kOverlayTextPrimary,
|
||||
14.0f);
|
||||
|
||||
@@ -484,8 +567,12 @@ void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path Application::ResolveRepoRelativePath(const char* relativePath) {
|
||||
return (std::filesystem::path(XCNEWEDITOR_REPO_ROOT) / relativePath).lexically_normal();
|
||||
std::filesystem::path Application::ResolveRepoRootPath() {
|
||||
std::string root = XCNEWEDITOR_REPO_ROOT;
|
||||
if (root.size() >= 2u && root.front() == '"' && root.back() == '"') {
|
||||
root = root.substr(1u, root.size() - 2u);
|
||||
}
|
||||
return std::filesystem::path(root).lexically_normal();
|
||||
}
|
||||
|
||||
LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
@@ -557,9 +644,40 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_SETFOCUS:
|
||||
if (application != nullptr) {
|
||||
application->m_inputModifierTracker.SyncFromSystemState();
|
||||
application->QueueWindowFocusEvent(UIInputEventType::FocusGained);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_KILLFOCUS:
|
||||
if (application != nullptr) {
|
||||
application->m_inputModifierTracker.Reset();
|
||||
application->QueueWindowFocusEvent(UIInputEventType::FocusLost);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_KEYDOWN:
|
||||
if (application != nullptr && wParam == VK_F12) {
|
||||
application->m_autoScreenshot.RequestCapture("manual_f12");
|
||||
case WM_SYSKEYDOWN:
|
||||
if (application != nullptr) {
|
||||
if (wParam == VK_F12) {
|
||||
application->m_autoScreenshot.RequestCapture("manual_f12");
|
||||
}
|
||||
application->QueueKeyEvent(UIInputEventType::KeyDown, wParam, lParam);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
if (application != nullptr) {
|
||||
application->QueueKeyEvent(UIInputEventType::KeyUp, wParam, lParam);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_CHAR:
|
||||
if (application != nullptr) {
|
||||
application->QueueCharacterEvent(wParam, lParam);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
@@ -579,9 +697,8 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
||||
}
|
||||
|
||||
int RunNewEditor(HINSTANCE hInstance, int nCmdShow) {
|
||||
Application application = {};
|
||||
Application application;
|
||||
return application.Run(hInstance, nCmdShow);
|
||||
}
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine::NewEditor
|
||||
@@ -4,8 +4,11 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "AutoScreenshot.h"
|
||||
#include "NativeRenderer.h"
|
||||
#include <XCNewEditor/Host/AutoScreenshot.h>
|
||||
#include <XCNewEditor/Host/InputModifierTracker.h>
|
||||
#include <XCNewEditor/Host/NativeRenderer.h>
|
||||
|
||||
#include "editor/EditorShellAsset.h"
|
||||
|
||||
#include <XCEngine/UI/Runtime/UIScreenDocumentHost.h>
|
||||
#include <XCEngine/UI/Runtime/UIScreenPlayer.h>
|
||||
@@ -19,8 +22,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
namespace XCEngine::NewEditor {
|
||||
|
||||
class Application {
|
||||
public:
|
||||
@@ -44,27 +46,32 @@ private:
|
||||
void QueuePointerEvent(::XCEngine::UI::UIInputEventType type, ::XCEngine::UI::UIPointerButton button, WPARAM wParam, LPARAM lParam);
|
||||
void QueuePointerLeaveEvent();
|
||||
void QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM lParam);
|
||||
void QueueKeyEvent(::XCEngine::UI::UIInputEventType type, WPARAM wParam, LPARAM lParam);
|
||||
void QueueCharacterEvent(WPARAM wParam, LPARAM lParam);
|
||||
void QueueWindowFocusEvent(::XCEngine::UI::UIInputEventType type);
|
||||
bool LoadStructuredScreen(const char* triggerReason);
|
||||
void RefreshStructuredScreen();
|
||||
void RebuildTrackedFileStates();
|
||||
bool DetectTrackedFileChange() const;
|
||||
void AppendRuntimeOverlay(::XCEngine::UI::UIDrawData& drawData, float width, float height) const;
|
||||
static std::filesystem::path ResolveRepoRelativePath(const char* relativePath);
|
||||
static std::filesystem::path ResolveRepoRootPath();
|
||||
|
||||
HWND m_hwnd = nullptr;
|
||||
HINSTANCE m_hInstance = nullptr;
|
||||
ATOM m_windowClassAtom = 0;
|
||||
NativeRenderer m_renderer;
|
||||
AutoScreenshotController m_autoScreenshot;
|
||||
::XCEngine::XCUI::Host::NativeRenderer m_renderer;
|
||||
::XCEngine::XCUI::Host::AutoScreenshotController m_autoScreenshot;
|
||||
::XCEngine::UI::Runtime::UIDocumentScreenHost m_documentHost;
|
||||
::XCEngine::UI::Runtime::UIScreenPlayer m_screenPlayer;
|
||||
::XCEngine::UI::Runtime::UIScreenAsset m_screenAsset = {};
|
||||
EditorShellAsset m_shellAssetDefinition = {};
|
||||
std::vector<TrackedFileState> m_trackedFiles = {};
|
||||
std::chrono::steady_clock::time_point m_startTime = {};
|
||||
std::chrono::steady_clock::time_point m_lastFrameTime = {};
|
||||
std::chrono::steady_clock::time_point m_lastReloadPollTime = {};
|
||||
std::uint64_t m_frameIndex = 0;
|
||||
std::vector<::XCEngine::UI::UIInputEvent> m_pendingInputEvents = {};
|
||||
::XCEngine::XCUI::Host::InputModifierTracker m_inputModifierTracker = {};
|
||||
bool m_trackingMouseLeave = false;
|
||||
bool m_useStructuredScreen = false;
|
||||
std::string m_runtimeStatus = {};
|
||||
@@ -73,5 +80,4 @@ private:
|
||||
|
||||
int RunNewEditor(HINSTANCE hInstance, int nCmdShow);
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine::NewEditor
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "AutoScreenshot.h"
|
||||
#include <XCNewEditor/Host/AutoScreenshot.h>
|
||||
|
||||
#include "NativeRenderer.h"
|
||||
#include <XCNewEditor/Host/NativeRenderer.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cctype>
|
||||
@@ -8,8 +8,7 @@
|
||||
#include <sstream>
|
||||
#include <system_error>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
namespace XCEngine::XCUI::Host {
|
||||
|
||||
void AutoScreenshotController::Initialize(const std::filesystem::path& captureRoot) {
|
||||
m_captureRoot = captureRoot.lexically_normal();
|
||||
@@ -144,5 +143,4 @@ std::string AutoScreenshotController::SanitizeReason(std::string_view reason) {
|
||||
return sanitized.empty() ? "capture" : sanitized;
|
||||
}
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine::XCUI::Host
|
||||
@@ -1,11 +1,10 @@
|
||||
#include "NativeRenderer.h"
|
||||
#include <XCNewEditor/Host/NativeRenderer.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
namespace XCEngine::XCUI::Host {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -483,5 +482,4 @@ std::wstring NativeRenderer::Utf8ToWide(std::string_view text) {
|
||||
return wideText;
|
||||
}
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine::XCUI::Host
|
||||
@@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <dwrite.h>
|
||||
#include <wincodec.h>
|
||||
#include <windows.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
|
||||
class NativeRenderer {
|
||||
public:
|
||||
bool Initialize(HWND hwnd);
|
||||
void Shutdown();
|
||||
void Resize(UINT width, UINT height);
|
||||
bool Render(const ::XCEngine::UI::UIDrawData& drawData);
|
||||
bool CaptureToPng(
|
||||
const ::XCEngine::UI::UIDrawData& drawData,
|
||||
UINT width,
|
||||
UINT height,
|
||||
const std::filesystem::path& outputPath,
|
||||
std::string& outError);
|
||||
|
||||
private:
|
||||
bool EnsureRenderTarget();
|
||||
bool EnsureWicFactory(std::string& outError);
|
||||
void DiscardRenderTarget();
|
||||
bool CreateDeviceResources();
|
||||
bool RenderToTarget(
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
ID2D1SolidColorBrush& solidBrush,
|
||||
const ::XCEngine::UI::UIDrawData& drawData);
|
||||
void RenderCommand(
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
ID2D1SolidColorBrush& solidBrush,
|
||||
const ::XCEngine::UI::UIDrawCommand& command,
|
||||
std::vector<D2D1_RECT_F>& clipStack);
|
||||
|
||||
IDWriteTextFormat* GetTextFormat(float fontSize);
|
||||
static D2D1_COLOR_F ToD2DColor(const ::XCEngine::UI::UIColor& color);
|
||||
static std::wstring Utf8ToWide(std::string_view text);
|
||||
|
||||
HWND m_hwnd = nullptr;
|
||||
Microsoft::WRL::ComPtr<ID2D1Factory> m_d2dFactory;
|
||||
Microsoft::WRL::ComPtr<IDWriteFactory> m_dwriteFactory;
|
||||
Microsoft::WRL::ComPtr<IWICImagingFactory> m_wicFactory;
|
||||
Microsoft::WRL::ComPtr<ID2D1HwndRenderTarget> m_renderTarget;
|
||||
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_solidBrush;
|
||||
std::unordered_map<int, Microsoft::WRL::ComPtr<IDWriteTextFormat>> m_textFormats;
|
||||
bool m_wicComInitialized = false;
|
||||
};
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
@@ -1,334 +0,0 @@
|
||||
#include "SandboxFrameBuilder.h"
|
||||
|
||||
#include <XCEngine/UI/Widgets/UIEditorPanelChrome.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::UI::UIColor;
|
||||
using ::XCEngine::UI::UIDrawData;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
using ::XCEngine::UI::Widgets::AppendUIEditorPanelChromeBackground;
|
||||
using ::XCEngine::UI::Widgets::AppendUIEditorPanelChromeForeground;
|
||||
using ::XCEngine::UI::Widgets::UIEditorPanelChromeMetrics;
|
||||
using ::XCEngine::UI::Widgets::UIEditorPanelChromeState;
|
||||
using ::XCEngine::UI::Widgets::UIEditorPanelChromeText;
|
||||
|
||||
constexpr UIColor kWorkspaceColor(0.05f, 0.07f, 0.09f, 1.0f);
|
||||
constexpr UIColor kShellColor(0.08f, 0.10f, 0.13f, 1.0f);
|
||||
constexpr UIColor kSubtleLineColor(0.18f, 0.23f, 0.29f, 1.0f);
|
||||
constexpr UIColor kTextPrimary(0.92f, 0.95f, 0.98f, 1.0f);
|
||||
constexpr UIColor kTextSecondary(0.67f, 0.74f, 0.81f, 1.0f);
|
||||
constexpr UIColor kTextMuted(0.49f, 0.56f, 0.64f, 1.0f);
|
||||
constexpr UIColor kSelectionColor(0.16f, 0.37f, 0.64f, 0.90f);
|
||||
constexpr UIColor kAccentColor(0.19f, 0.76f, 0.57f, 1.0f);
|
||||
constexpr UIColor kWarningColor(0.96f, 0.72f, 0.22f, 1.0f);
|
||||
constexpr UIColor kErrorColor(0.88f, 0.29f, 0.30f, 1.0f);
|
||||
|
||||
float ClampPositive(float value, float fallback) {
|
||||
return value > 1.0f ? value : fallback;
|
||||
}
|
||||
|
||||
float Pulse01(double timeSeconds, double speed) {
|
||||
const double phase = std::sin(timeSeconds * speed);
|
||||
return static_cast<float>(0.5 + phase * 0.5);
|
||||
}
|
||||
|
||||
UIRect InsetRect(const UIRect& rect, float insetX, float insetY) {
|
||||
return UIRect(
|
||||
rect.x + insetX,
|
||||
rect.y + insetY,
|
||||
(std::max)(0.0f, rect.width - insetX * 2.0f),
|
||||
(std::max)(0.0f, rect.height - insetY * 2.0f));
|
||||
}
|
||||
|
||||
void AddSectionTitle(
|
||||
UIDrawList& drawList,
|
||||
float x,
|
||||
float y,
|
||||
std::string_view title,
|
||||
std::string_view subtitle = {}) {
|
||||
drawList.AddText(UIPoint(x, y), std::string(title), kTextPrimary, 17.0f);
|
||||
if (!subtitle.empty()) {
|
||||
drawList.AddText(UIPoint(x, y + 20.0f), std::string(subtitle), kTextMuted, 13.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AddRow(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& rect,
|
||||
std::string_view label,
|
||||
bool selected = false,
|
||||
float indent = 0.0f,
|
||||
const UIColor& dotColor = kAccentColor) {
|
||||
if (selected) {
|
||||
drawList.AddFilledRect(rect, kSelectionColor, 6.0f);
|
||||
}
|
||||
|
||||
drawList.AddFilledRect(
|
||||
UIRect(rect.x + 10.0f + indent, rect.y + 10.0f, 8.0f, 8.0f),
|
||||
dotColor,
|
||||
4.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(rect.x + 26.0f + indent, rect.y + 6.0f),
|
||||
std::string(label),
|
||||
selected ? kTextPrimary : kTextSecondary,
|
||||
14.0f);
|
||||
}
|
||||
|
||||
void AddPropertyRow(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& rect,
|
||||
std::string_view name,
|
||||
std::string_view value) {
|
||||
drawList.AddText(UIPoint(rect.x, rect.y + 6.0f), std::string(name), kTextSecondary, 14.0f);
|
||||
|
||||
const UIRect fieldRect(rect.x + rect.width * 0.42f, rect.y, rect.width * 0.58f, rect.height);
|
||||
drawList.AddFilledRect(fieldRect, UIColor(0.11f, 0.15f, 0.20f, 1.0f), 6.0f);
|
||||
drawList.AddRectOutline(fieldRect, kSubtleLineColor, 1.0f, 6.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(fieldRect.x + 10.0f, fieldRect.y + 6.0f),
|
||||
std::string(value),
|
||||
kTextPrimary,
|
||||
14.0f);
|
||||
}
|
||||
|
||||
void AddLogRow(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& rect,
|
||||
const UIColor& levelColor,
|
||||
std::string_view level,
|
||||
std::string_view message) {
|
||||
drawList.AddFilledRect(
|
||||
UIRect(rect.x, rect.y + 7.0f, 6.0f, rect.height - 14.0f),
|
||||
levelColor,
|
||||
3.0f);
|
||||
drawList.AddText(UIPoint(rect.x + 16.0f, rect.y + 4.0f), std::string(level), levelColor, 13.0f);
|
||||
drawList.AddText(UIPoint(rect.x + 82.0f, rect.y + 4.0f), std::string(message), kTextSecondary, 13.0f);
|
||||
}
|
||||
|
||||
void AppendPanel(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& panelRect,
|
||||
std::string_view title,
|
||||
std::string_view subtitle,
|
||||
std::string_view footer,
|
||||
bool active) {
|
||||
UIEditorPanelChromeState state = {};
|
||||
state.active = active;
|
||||
state.hovered = active;
|
||||
|
||||
UIEditorPanelChromeText text = {};
|
||||
text.title = title;
|
||||
text.subtitle = subtitle;
|
||||
text.footer = footer;
|
||||
|
||||
UIEditorPanelChromeMetrics metrics = {};
|
||||
metrics.headerHeight = 46.0f;
|
||||
|
||||
AppendUIEditorPanelChromeBackground(drawList, panelRect, state, {}, metrics);
|
||||
AppendUIEditorPanelChromeForeground(drawList, panelRect, text, {}, metrics);
|
||||
}
|
||||
|
||||
void AppendTopBar(UIDrawList& drawList, const UIRect& rect, double timeSeconds) {
|
||||
drawList.AddFilledRect(rect, kShellColor, 0.0f);
|
||||
drawList.AddRectOutline(rect, kSubtleLineColor, 1.0f, 0.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(rect.x + 20.0f, rect.y + 14.0f),
|
||||
"XCUI Native Sandbox",
|
||||
kTextPrimary,
|
||||
20.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(rect.x + 228.0f, rect.y + 17.0f),
|
||||
"editor ui proving ground",
|
||||
kTextMuted,
|
||||
13.0f);
|
||||
|
||||
const float pulse = Pulse01(timeSeconds, 2.2);
|
||||
const UIColor liveColor(
|
||||
0.18f + pulse * 0.16f,
|
||||
0.74f,
|
||||
0.58f,
|
||||
1.0f);
|
||||
drawList.AddFilledRect(
|
||||
UIRect(rect.x + rect.width - 170.0f, rect.y + 12.0f, 108.0f, 24.0f),
|
||||
UIColor(0.10f, 0.16f, 0.12f, 1.0f),
|
||||
12.0f);
|
||||
drawList.AddFilledRect(
|
||||
UIRect(rect.x + rect.width - 160.0f, rect.y + 20.0f, 8.0f, 8.0f),
|
||||
liveColor,
|
||||
4.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(rect.x + rect.width - 146.0f, rect.y + 14.0f),
|
||||
"Native Render",
|
||||
kTextPrimary,
|
||||
13.0f);
|
||||
}
|
||||
|
||||
void AppendStatusBar(UIDrawList& drawList, const UIRect& rect, const SandboxFrameOptions& options) {
|
||||
drawList.AddFilledRect(rect, kShellColor, 0.0f);
|
||||
drawList.AddRectOutline(rect, kSubtleLineColor, 1.0f, 0.0f);
|
||||
|
||||
const std::string leftText =
|
||||
"Direct renderer | size " +
|
||||
std::to_string(static_cast<int>(options.width)) +
|
||||
"x" +
|
||||
std::to_string(static_cast<int>(options.height));
|
||||
drawList.AddText(UIPoint(rect.x + 16.0f, rect.y + 8.0f), leftText, kTextSecondary, 13.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(rect.x + rect.width - 176.0f, rect.y + 8.0f),
|
||||
"No ImGui Host",
|
||||
kAccentColor,
|
||||
13.0f);
|
||||
}
|
||||
|
||||
void AppendHierarchyPanel(UIDrawList& drawList, const UIRect& panelRect) {
|
||||
AppendPanel(drawList, panelRect, "Hierarchy", "scene graph", "12 items", false);
|
||||
|
||||
const UIRect body = InsetRect(panelRect, 16.0f, 60.0f);
|
||||
AddSectionTitle(drawList, body.x, body.y, "Active Scene", "Sandbox_City.xcscene");
|
||||
|
||||
float rowY = body.y + 42.0f;
|
||||
const float rowHeight = 28.0f;
|
||||
AddRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "World", false, 0.0f, kWarningColor);
|
||||
rowY += rowHeight + 4.0f;
|
||||
AddRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Main Camera", false, 18.0f);
|
||||
rowY += rowHeight + 4.0f;
|
||||
AddRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Directional Light", false, 18.0f, kWarningColor);
|
||||
rowY += rowHeight + 4.0f;
|
||||
AddRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Player", true, 18.0f);
|
||||
rowY += rowHeight + 4.0f;
|
||||
AddRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "WeaponSocket", false, 36.0f, UIColor(0.58f, 0.70f, 0.88f, 1.0f));
|
||||
rowY += rowHeight + 4.0f;
|
||||
AddRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "FX_Trail", false, 36.0f, UIColor(0.76f, 0.43f, 0.93f, 1.0f));
|
||||
}
|
||||
|
||||
void AppendInspectorPanel(UIDrawList& drawList, const UIRect& panelRect) {
|
||||
AppendPanel(drawList, panelRect, "Inspector", "Player", "schema-first draft", false);
|
||||
|
||||
const UIRect body = InsetRect(panelRect, 16.0f, 60.0f);
|
||||
AddSectionTitle(drawList, body.x, body.y, "Transform", "shared widget experiment");
|
||||
|
||||
float rowY = body.y + 42.0f;
|
||||
const float rowHeight = 30.0f;
|
||||
AddPropertyRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Position", "12.0, 1.8, -4.0");
|
||||
rowY += rowHeight + 6.0f;
|
||||
AddPropertyRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Rotation", "0.0, 36.5, 0.0");
|
||||
rowY += rowHeight + 6.0f;
|
||||
AddPropertyRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Scale", "1.0, 1.0, 1.0");
|
||||
rowY += 48.0f;
|
||||
|
||||
AddSectionTitle(drawList, body.x, rowY, "Character", "future AutoForm target");
|
||||
rowY += 42.0f;
|
||||
AddPropertyRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "State", "Locomotion");
|
||||
rowY += rowHeight + 6.0f;
|
||||
AddPropertyRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Move Speed", "6.4");
|
||||
rowY += rowHeight + 6.0f;
|
||||
AddPropertyRow(drawList, UIRect(body.x, rowY, body.width, rowHeight), "Jump Height", "1.3");
|
||||
}
|
||||
|
||||
void AppendScenePanel(UIDrawList& drawList, const UIRect& panelRect, double timeSeconds) {
|
||||
AppendPanel(drawList, panelRect, "Scene", "native shell draft", "ViewportSlot comes later", true);
|
||||
|
||||
const UIRect body = InsetRect(panelRect, 16.0f, 60.0f);
|
||||
drawList.AddFilledRect(body, UIColor(0.07f, 0.09f, 0.11f, 1.0f), 12.0f);
|
||||
drawList.AddRectOutline(body, kSubtleLineColor, 1.0f, 12.0f);
|
||||
|
||||
const float gridStep = 34.0f;
|
||||
for (float x = body.x; x < body.x + body.width; x += gridStep) {
|
||||
drawList.AddRectOutline(UIRect(x, body.y, 1.0f, body.height), UIColor(0.10f, 0.13f, 0.16f, 0.6f));
|
||||
}
|
||||
for (float y = body.y; y < body.y + body.height; y += gridStep) {
|
||||
drawList.AddRectOutline(UIRect(body.x, y, body.width, 1.0f), UIColor(0.10f, 0.13f, 0.16f, 0.6f));
|
||||
}
|
||||
|
||||
const float pulse = Pulse01(timeSeconds, 1.8);
|
||||
const UIRect selectionRect(
|
||||
body.x + body.width * 0.32f,
|
||||
body.y + body.height * 0.24f,
|
||||
body.width * 0.22f,
|
||||
body.height * 0.34f);
|
||||
drawList.AddRectOutline(
|
||||
selectionRect,
|
||||
UIColor(0.24f + pulse * 0.12f, 0.56f, 0.95f, 1.0f),
|
||||
2.0f,
|
||||
8.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(body.x + 18.0f, body.y + 18.0f),
|
||||
"This area is intentionally native-rendered, not hosted by ImGui.",
|
||||
kTextSecondary,
|
||||
15.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(body.x + 18.0f, body.y + 40.0f),
|
||||
"Use this sandbox to iterate shell chrome, panel composition, and draw packets.",
|
||||
kTextMuted,
|
||||
13.0f);
|
||||
}
|
||||
|
||||
void AppendConsolePanel(UIDrawList& drawList, const UIRect& panelRect) {
|
||||
AppendPanel(drawList, panelRect, "Console", "native renderer smoke log", "6 records", false);
|
||||
|
||||
const UIRect body = InsetRect(panelRect, 16.0f, 60.0f);
|
||||
AddLogRow(drawList, UIRect(body.x, body.y, body.width, 22.0f), kAccentColor, "Info", "XCUI native sandbox frame submitted.");
|
||||
AddLogRow(drawList, UIRect(body.x, body.y + 26.0f, body.width, 22.0f), kWarningColor, "Warn", "Viewport host not connected in this phase.");
|
||||
AddLogRow(drawList, UIRect(body.x, body.y + 52.0f, body.width, 22.0f), kTextSecondary, "Info", "Hierarchy / Inspector are draw-packet prototypes.");
|
||||
AddLogRow(drawList, UIRect(body.x, body.y + 78.0f, body.width, 22.0f), kErrorColor, "Todo", "Replace bespoke scene panel with authored shell document.");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UIDrawData BuildSandboxFrame(const SandboxFrameOptions& options) {
|
||||
const float width = ClampPositive(options.width, 1280.0f);
|
||||
const float height = ClampPositive(options.height, 720.0f);
|
||||
|
||||
const float margin = 18.0f;
|
||||
const float gap = 14.0f;
|
||||
const float topBarHeight = 48.0f;
|
||||
const float statusBarHeight = 30.0f;
|
||||
const float leftPanelWidth = 260.0f;
|
||||
const float rightPanelWidth = 320.0f;
|
||||
const float bottomPanelHeight = 210.0f;
|
||||
|
||||
const UIRect fullRect(0.0f, 0.0f, width, height);
|
||||
const UIRect topBarRect(0.0f, 0.0f, width, topBarHeight);
|
||||
const UIRect statusRect(0.0f, height - statusBarHeight, width, statusBarHeight);
|
||||
|
||||
const float workspaceTop = topBarHeight + margin;
|
||||
const float workspaceBottom = height - statusBarHeight - margin;
|
||||
const float workspaceHeight = (std::max)(0.0f, workspaceBottom - workspaceTop);
|
||||
const float centerX = margin + leftPanelWidth + gap;
|
||||
const float centerWidth = (std::max)(240.0f, width - margin * 2.0f - leftPanelWidth - rightPanelWidth - gap * 2.0f);
|
||||
const float sceneHeight = (std::max)(220.0f, workspaceHeight - bottomPanelHeight - gap);
|
||||
|
||||
const UIRect hierarchyRect(margin, workspaceTop, leftPanelWidth, workspaceHeight);
|
||||
const UIRect sceneRect(centerX, workspaceTop, centerWidth, sceneHeight);
|
||||
const UIRect consoleRect(centerX, workspaceTop + sceneHeight + gap, centerWidth, bottomPanelHeight);
|
||||
const UIRect inspectorRect(centerX + centerWidth + gap, workspaceTop, rightPanelWidth, workspaceHeight);
|
||||
|
||||
UIDrawData drawData = {};
|
||||
UIDrawList& drawList = drawData.EmplaceDrawList("XCUI Native Editor Sandbox");
|
||||
drawList.PushClipRect(fullRect);
|
||||
drawList.AddFilledRect(fullRect, kWorkspaceColor, 0.0f);
|
||||
|
||||
AppendTopBar(drawList, topBarRect, options.timeSeconds);
|
||||
AppendHierarchyPanel(drawList, hierarchyRect);
|
||||
AppendScenePanel(drawList, sceneRect, options.timeSeconds);
|
||||
AppendConsolePanel(drawList, consoleRect);
|
||||
AppendInspectorPanel(drawList, inspectorRect);
|
||||
AppendStatusBar(drawList, statusRect, options);
|
||||
|
||||
drawList.PopClipRect();
|
||||
return drawData;
|
||||
}
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
|
||||
struct SandboxFrameOptions {
|
||||
float width = 1280.0f;
|
||||
float height = 720.0f;
|
||||
double timeSeconds = 0.0;
|
||||
};
|
||||
|
||||
::XCEngine::UI::UIDrawData BuildSandboxFrame(const SandboxFrameOptions& options);
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
115
new_editor/src/Widgets/UIEditorCollectionPrimitives.cpp
Normal file
115
new_editor/src/Widgets/UIEditorCollectionPrimitives.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <XCNewEditor/Widgets/UIEditorCollectionPrimitives.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
namespace {
|
||||
|
||||
float ResolveFloatToken(
|
||||
const Style::UITheme& theme,
|
||||
const char* tokenName,
|
||||
float fallbackValue) {
|
||||
const Style::UITokenResolveResult result =
|
||||
theme.ResolveToken(tokenName, Style::UIStyleValueType::Float);
|
||||
if (result.status != Style::UITokenResolveStatus::Resolved) {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
const float* value = result.value.TryGetFloat();
|
||||
return value != nullptr ? *value : fallbackValue;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UIEditorCollectionPrimitiveKind ClassifyUIEditorCollectionPrimitive(std::string_view tagName) {
|
||||
if (tagName == "ScrollView") {
|
||||
return UIEditorCollectionPrimitiveKind::ScrollView;
|
||||
}
|
||||
if (tagName == "TreeView") {
|
||||
return UIEditorCollectionPrimitiveKind::TreeView;
|
||||
}
|
||||
if (tagName == "TreeItem") {
|
||||
return UIEditorCollectionPrimitiveKind::TreeItem;
|
||||
}
|
||||
if (tagName == "ListView") {
|
||||
return UIEditorCollectionPrimitiveKind::ListView;
|
||||
}
|
||||
if (tagName == "ListItem") {
|
||||
return UIEditorCollectionPrimitiveKind::ListItem;
|
||||
}
|
||||
if (tagName == "PropertySection") {
|
||||
return UIEditorCollectionPrimitiveKind::PropertySection;
|
||||
}
|
||||
if (tagName == "FieldRow") {
|
||||
return UIEditorCollectionPrimitiveKind::FieldRow;
|
||||
}
|
||||
|
||||
return UIEditorCollectionPrimitiveKind::None;
|
||||
}
|
||||
|
||||
bool IsUIEditorCollectionPrimitiveContainer(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::ScrollView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection;
|
||||
}
|
||||
|
||||
bool UsesUIEditorCollectionPrimitiveColumnLayout(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection;
|
||||
}
|
||||
|
||||
bool IsUIEditorCollectionPrimitiveHoverable(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeItem ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListItem ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection ||
|
||||
kind == UIEditorCollectionPrimitiveKind::FieldRow;
|
||||
}
|
||||
|
||||
bool DoesUIEditorCollectionPrimitiveClipChildren(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::ScrollView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView;
|
||||
}
|
||||
|
||||
float ResolveUIEditorCollectionPrimitivePadding(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection
|
||||
? ResolveFloatToken(theme, "space.cardInset", 12.0f)
|
||||
: 0.0f;
|
||||
}
|
||||
|
||||
float ResolveUIEditorCollectionPrimitiveDefaultHeight(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme) {
|
||||
switch (kind) {
|
||||
case UIEditorCollectionPrimitiveKind::TreeItem:
|
||||
return ResolveFloatToken(theme, "size.treeItemHeight", 28.0f);
|
||||
case UIEditorCollectionPrimitiveKind::ListItem:
|
||||
return ResolveFloatToken(theme, "size.listItemHeight", 60.0f);
|
||||
case UIEditorCollectionPrimitiveKind::FieldRow:
|
||||
return ResolveFloatToken(theme, "size.fieldRowHeight", 32.0f);
|
||||
case UIEditorCollectionPrimitiveKind::PropertySection:
|
||||
return ResolveFloatToken(theme, "size.propertySectionHeight", 148.0f);
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float ResolveUIEditorCollectionPrimitiveIndent(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme,
|
||||
float indentLevel) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeItem
|
||||
? indentLevel * ResolveFloatToken(theme, "size.treeIndent", 18.0f)
|
||||
: 0.0f;
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
13
new_editor/src/editor/EditorShellAsset.cpp
Normal file
13
new_editor/src/editor/EditorShellAsset.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "EditorShellAsset.h"
|
||||
|
||||
namespace XCEngine::NewEditor {
|
||||
|
||||
EditorShellAsset BuildDefaultEditorShellAsset(const std::filesystem::path& repoRoot) {
|
||||
EditorShellAsset asset = {};
|
||||
asset.documentPath = (repoRoot / "new_editor/ui/views/editor_shell.xcui").lexically_normal();
|
||||
asset.themePath = (repoRoot / "new_editor/ui/themes/editor_shell.xctheme").lexically_normal();
|
||||
asset.captureRootPath = (repoRoot / "new_editor/captures").lexically_normal();
|
||||
return asset;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::NewEditor
|
||||
17
new_editor/src/editor/EditorShellAsset.h
Normal file
17
new_editor/src/editor/EditorShellAsset.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine::NewEditor {
|
||||
|
||||
struct EditorShellAsset {
|
||||
std::string screenId = "new_editor.shell";
|
||||
std::filesystem::path documentPath = {};
|
||||
std::filesystem::path themePath = {};
|
||||
std::filesystem::path captureRootPath = {};
|
||||
};
|
||||
|
||||
EditorShellAsset BuildDefaultEditorShellAsset(const std::filesystem::path& repoRoot);
|
||||
|
||||
} // namespace XCEngine::NewEditor
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Application.h"
|
||||
#include "Host/Application.h"
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow) {
|
||||
return XCEngine::NewEditor::RunNewEditor(hInstance, nCmdShow);
|
||||
|
||||
Reference in New Issue
Block a user