Build XCUI splitter foundation and test harness
This commit is contained in:
@@ -9,15 +9,16 @@ file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}" XCNEWEDITOR_REPO_ROOT_PATH)
|
||||
set(NEW_EDITOR_RESOURCE_FILES
|
||||
ui/views/editor_shell.xcui
|
||||
ui/themes/editor_shell.xctheme
|
||||
ui/schemas/editor_inspector_shell.xcschema
|
||||
)
|
||||
|
||||
add_library(XCNewEditorLib STATIC
|
||||
src/SandboxFrameBuilder.cpp
|
||||
src/editor/EditorShellAsset.cpp
|
||||
src/Widgets/UIEditorCollectionPrimitives.cpp
|
||||
)
|
||||
|
||||
target_include_directories(XCNewEditorLib
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
)
|
||||
@@ -25,6 +26,7 @@ target_include_directories(XCNewEditorLib
|
||||
target_compile_definitions(XCNewEditorLib PUBLIC
|
||||
UNICODE
|
||||
_UNICODE
|
||||
XCNEWEDITOR_REPO_ROOT="${XCNEWEDITOR_REPO_ROOT_PATH}"
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
@@ -37,41 +39,70 @@ target_link_libraries(XCNewEditorLib PUBLIC
|
||||
XCEngine
|
||||
)
|
||||
|
||||
add_executable(XCNewEditorApp WIN32
|
||||
src/main.cpp
|
||||
src/Application.cpp
|
||||
src/AutoScreenshot.cpp
|
||||
src/NativeRenderer.cpp
|
||||
${NEW_EDITOR_RESOURCE_FILES}
|
||||
add_library(XCNewEditorHost STATIC
|
||||
src/Host/AutoScreenshot.cpp
|
||||
src/Host/NativeRenderer.cpp
|
||||
)
|
||||
|
||||
target_include_directories(XCNewEditorApp PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
target_include_directories(XCNewEditorHost
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
)
|
||||
|
||||
target_compile_definitions(XCNewEditorApp PRIVATE
|
||||
target_compile_definitions(XCNewEditorHost PUBLIC
|
||||
UNICODE
|
||||
_UNICODE
|
||||
XCNEWEDITOR_REPO_ROOT="${XCNEWEDITOR_REPO_ROOT_PATH}"
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(XCNewEditorApp PRIVATE /utf-8 /FS)
|
||||
set_property(TARGET XCNewEditorApp PROPERTY
|
||||
target_compile_options(XCNewEditorHost PRIVATE /utf-8 /FS)
|
||||
set_property(TARGET XCNewEditorHost PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
endif()
|
||||
|
||||
target_link_libraries(XCNewEditorApp PRIVATE
|
||||
XCNewEditorLib
|
||||
target_link_libraries(XCNewEditorHost PUBLIC
|
||||
XCEngine
|
||||
d2d1.lib
|
||||
dwrite.lib
|
||||
windowscodecs.lib
|
||||
)
|
||||
|
||||
set_target_properties(XCNewEditorApp PROPERTIES
|
||||
OUTPUT_NAME "XCNewEditor"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin"
|
||||
)
|
||||
if(XCENGINE_BUILD_NEW_EDITOR)
|
||||
add_executable(XCNewEditorApp WIN32
|
||||
src/main.cpp
|
||||
src/Host/Application.cpp
|
||||
${NEW_EDITOR_RESOURCE_FILES}
|
||||
)
|
||||
|
||||
target_include_directories(XCNewEditorApp PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
)
|
||||
|
||||
target_compile_definitions(XCNewEditorApp PRIVATE
|
||||
UNICODE
|
||||
_UNICODE
|
||||
XCNEWEDITOR_REPO_ROOT="${XCNEWEDITOR_REPO_ROOT_PATH}"
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(XCNewEditorApp PRIVATE /utf-8 /FS)
|
||||
set_property(TARGET XCNewEditorApp PROPERTY
|
||||
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
endif()
|
||||
|
||||
target_link_libraries(XCNewEditorApp PRIVATE
|
||||
XCNewEditorLib
|
||||
XCNewEditorHost
|
||||
)
|
||||
|
||||
set_target_properties(XCNewEditorApp PROPERTIES
|
||||
OUTPUT_NAME "XCNewEditor"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin"
|
||||
)
|
||||
endif()
|
||||
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${NEW_EDITOR_RESOURCE_FILES})
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
namespace XCEngine::XCUI::Host {
|
||||
|
||||
class NativeRenderer;
|
||||
|
||||
@@ -50,5 +49,4 @@ private:
|
||||
bool m_capturePending = false;
|
||||
};
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine::XCUI::Host
|
||||
173
new_editor/include/XCNewEditor/Host/InputModifierTracker.h
Normal file
173
new_editor/include/XCNewEditor/Host/InputModifierTracker.h
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine::XCUI::Host {
|
||||
|
||||
class InputModifierTracker {
|
||||
public:
|
||||
void Reset() {
|
||||
m_leftShift = false;
|
||||
m_rightShift = false;
|
||||
m_leftControl = false;
|
||||
m_rightControl = false;
|
||||
m_leftAlt = false;
|
||||
m_rightAlt = false;
|
||||
m_leftSuper = false;
|
||||
m_rightSuper = false;
|
||||
}
|
||||
|
||||
void SyncFromSystemState() {
|
||||
m_leftShift = (GetKeyState(VK_LSHIFT) & 0x8000) != 0;
|
||||
m_rightShift = (GetKeyState(VK_RSHIFT) & 0x8000) != 0;
|
||||
m_leftControl = (GetKeyState(VK_LCONTROL) & 0x8000) != 0;
|
||||
m_rightControl = (GetKeyState(VK_RCONTROL) & 0x8000) != 0;
|
||||
m_leftAlt = (GetKeyState(VK_LMENU) & 0x8000) != 0;
|
||||
m_rightAlt = (GetKeyState(VK_RMENU) & 0x8000) != 0;
|
||||
m_leftSuper = (GetKeyState(VK_LWIN) & 0x8000) != 0;
|
||||
m_rightSuper = (GetKeyState(VK_RWIN) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIInputModifiers GetCurrentModifiers() const {
|
||||
return BuildModifiers();
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIInputModifiers BuildPointerModifiers(std::size_t wParam) const {
|
||||
::XCEngine::UI::UIInputModifiers modifiers = BuildModifiers();
|
||||
modifiers.shift = modifiers.shift || (wParam & MK_SHIFT) != 0;
|
||||
modifiers.control = modifiers.control || (wParam & MK_CONTROL) != 0;
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIInputModifiers ApplyKeyMessage(
|
||||
::XCEngine::UI::UIInputEventType type,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
if (type == ::XCEngine::UI::UIInputEventType::KeyDown) {
|
||||
SetModifierState(ResolveModifierKey(wParam, lParam), true);
|
||||
} else if (type == ::XCEngine::UI::UIInputEventType::KeyUp) {
|
||||
SetModifierState(ResolveModifierKey(wParam, lParam), false);
|
||||
}
|
||||
|
||||
return BuildModifiers();
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ModifierKey : std::uint8_t {
|
||||
None = 0,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
LeftControl,
|
||||
RightControl,
|
||||
LeftAlt,
|
||||
RightAlt,
|
||||
LeftSuper,
|
||||
RightSuper
|
||||
};
|
||||
|
||||
static bool IsExtendedKey(LPARAM lParam) {
|
||||
return (static_cast<std::uint32_t>(lParam) & 0x01000000u) != 0u;
|
||||
}
|
||||
|
||||
static std::uint32_t ExtractScanCode(LPARAM lParam) {
|
||||
return (static_cast<std::uint32_t>(lParam) >> 16u) & 0xffu;
|
||||
}
|
||||
|
||||
static ModifierKey ResolveModifierKey(WPARAM wParam, LPARAM lParam) {
|
||||
switch (static_cast<std::uint32_t>(wParam)) {
|
||||
case VK_SHIFT: {
|
||||
const UINT shiftVirtualKey = MapVirtualKeyW(ExtractScanCode(lParam), MAPVK_VSC_TO_VK_EX);
|
||||
return shiftVirtualKey == VK_RSHIFT
|
||||
? ModifierKey::RightShift
|
||||
: ModifierKey::LeftShift;
|
||||
}
|
||||
case VK_LSHIFT:
|
||||
return ModifierKey::LeftShift;
|
||||
case VK_RSHIFT:
|
||||
return ModifierKey::RightShift;
|
||||
case VK_CONTROL:
|
||||
return IsExtendedKey(lParam)
|
||||
? ModifierKey::RightControl
|
||||
: ModifierKey::LeftControl;
|
||||
case VK_LCONTROL:
|
||||
return ModifierKey::LeftControl;
|
||||
case VK_RCONTROL:
|
||||
return ModifierKey::RightControl;
|
||||
case VK_MENU:
|
||||
return IsExtendedKey(lParam)
|
||||
? ModifierKey::RightAlt
|
||||
: ModifierKey::LeftAlt;
|
||||
case VK_LMENU:
|
||||
return ModifierKey::LeftAlt;
|
||||
case VK_RMENU:
|
||||
return ModifierKey::RightAlt;
|
||||
case VK_LWIN:
|
||||
return ModifierKey::LeftSuper;
|
||||
case VK_RWIN:
|
||||
return ModifierKey::RightSuper;
|
||||
default:
|
||||
return ModifierKey::None;
|
||||
}
|
||||
}
|
||||
|
||||
void SetModifierState(ModifierKey key, bool pressed) {
|
||||
switch (key) {
|
||||
case ModifierKey::LeftShift:
|
||||
m_leftShift = pressed;
|
||||
break;
|
||||
case ModifierKey::RightShift:
|
||||
m_rightShift = pressed;
|
||||
break;
|
||||
case ModifierKey::LeftControl:
|
||||
m_leftControl = pressed;
|
||||
break;
|
||||
case ModifierKey::RightControl:
|
||||
m_rightControl = pressed;
|
||||
break;
|
||||
case ModifierKey::LeftAlt:
|
||||
m_leftAlt = pressed;
|
||||
break;
|
||||
case ModifierKey::RightAlt:
|
||||
m_rightAlt = pressed;
|
||||
break;
|
||||
case ModifierKey::LeftSuper:
|
||||
m_leftSuper = pressed;
|
||||
break;
|
||||
case ModifierKey::RightSuper:
|
||||
m_rightSuper = pressed;
|
||||
break;
|
||||
case ModifierKey::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIInputModifiers BuildModifiers() const {
|
||||
::XCEngine::UI::UIInputModifiers modifiers = {};
|
||||
modifiers.shift = m_leftShift || m_rightShift;
|
||||
modifiers.control = m_leftControl || m_rightControl;
|
||||
modifiers.alt = m_leftAlt || m_rightAlt;
|
||||
modifiers.super = m_leftSuper || m_rightSuper;
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
bool m_leftShift = false;
|
||||
bool m_rightShift = false;
|
||||
bool m_leftControl = false;
|
||||
bool m_rightControl = false;
|
||||
bool m_leftAlt = false;
|
||||
bool m_rightAlt = false;
|
||||
bool m_leftSuper = false;
|
||||
bool m_rightSuper = false;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::XCUI::Host
|
||||
@@ -18,8 +18,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace NewEditor {
|
||||
namespace XCEngine::XCUI::Host {
|
||||
|
||||
class NativeRenderer {
|
||||
public:
|
||||
@@ -63,5 +62,4 @@ private:
|
||||
bool m_wicComInitialized = false;
|
||||
};
|
||||
|
||||
} // namespace NewEditor
|
||||
} // namespace XCEngine
|
||||
} // namespace XCEngine::XCUI::Host
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/Style/Theme.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
enum class UIEditorCollectionPrimitiveKind : std::uint8_t {
|
||||
None = 0,
|
||||
ScrollView,
|
||||
TreeView,
|
||||
TreeItem,
|
||||
ListView,
|
||||
ListItem,
|
||||
PropertySection,
|
||||
FieldRow
|
||||
};
|
||||
|
||||
UIEditorCollectionPrimitiveKind ClassifyUIEditorCollectionPrimitive(std::string_view tagName);
|
||||
bool IsUIEditorCollectionPrimitiveContainer(UIEditorCollectionPrimitiveKind kind);
|
||||
bool UsesUIEditorCollectionPrimitiveColumnLayout(UIEditorCollectionPrimitiveKind kind);
|
||||
bool IsUIEditorCollectionPrimitiveHoverable(UIEditorCollectionPrimitiveKind kind);
|
||||
bool DoesUIEditorCollectionPrimitiveClipChildren(UIEditorCollectionPrimitiveKind kind);
|
||||
float ResolveUIEditorCollectionPrimitivePadding(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme);
|
||||
float ResolveUIEditorCollectionPrimitiveDefaultHeight(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme);
|
||||
float ResolveUIEditorCollectionPrimitiveIndent(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme,
|
||||
float indentLevel);
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
126
new_editor/include/XCNewEditor/Widgets/UIEditorPanelChrome.h
Normal file
126
new_editor/include/XCNewEditor/Widgets/UIEditorPanelChrome.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
struct UIEditorPanelChromeState {
|
||||
bool active = false;
|
||||
bool hovered = false;
|
||||
};
|
||||
|
||||
struct UIEditorPanelChromeText {
|
||||
std::string_view title = {};
|
||||
std::string_view subtitle = {};
|
||||
std::string_view footer = {};
|
||||
};
|
||||
|
||||
struct UIEditorPanelChromeMetrics {
|
||||
float cornerRounding = 18.0f;
|
||||
float headerHeight = 42.0f;
|
||||
float titleInsetX = 16.0f;
|
||||
float titleInsetY = 12.0f;
|
||||
float subtitleInsetY = 28.0f;
|
||||
float footerInsetX = 16.0f;
|
||||
float footerInsetBottom = 18.0f;
|
||||
float activeBorderThickness = 2.0f;
|
||||
float inactiveBorderThickness = 1.0f;
|
||||
};
|
||||
|
||||
struct UIEditorPanelChromePalette {
|
||||
UIColor surfaceColor = UIColor(9.0f / 255.0f, 13.0f / 255.0f, 18.0f / 255.0f, 212.0f / 255.0f);
|
||||
UIColor borderColor = UIColor(53.0f / 255.0f, 72.0f / 255.0f, 96.0f / 255.0f, 1.0f);
|
||||
UIColor accentColor = UIColor(84.0f / 255.0f, 176.0f / 255.0f, 244.0f / 255.0f, 1.0f);
|
||||
UIColor hoveredAccentColor = UIColor(1.0f, 206.0f / 255.0f, 112.0f / 255.0f, 1.0f);
|
||||
UIColor headerColor = UIColor(13.0f / 255.0f, 20.0f / 255.0f, 28.0f / 255.0f, 242.0f / 255.0f);
|
||||
UIColor textPrimary = UIColor(232.0f / 255.0f, 238.0f / 255.0f, 246.0f / 255.0f, 1.0f);
|
||||
UIColor textSecondary = UIColor(150.0f / 255.0f, 164.0f / 255.0f, 184.0f / 255.0f, 1.0f);
|
||||
UIColor textMuted = UIColor(108.0f / 255.0f, 123.0f / 255.0f, 145.0f / 255.0f, 1.0f);
|
||||
};
|
||||
|
||||
inline UIRect BuildUIEditorPanelChromeHeaderRect(
|
||||
const UIRect& panelRect,
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
return UIRect(
|
||||
panelRect.x,
|
||||
panelRect.y,
|
||||
panelRect.width,
|
||||
metrics.headerHeight);
|
||||
}
|
||||
|
||||
inline UIColor ResolveUIEditorPanelChromeBorderColor(
|
||||
const UIEditorPanelChromeState& state,
|
||||
const UIEditorPanelChromePalette& palette = {}) {
|
||||
if (state.active) {
|
||||
return palette.accentColor;
|
||||
}
|
||||
|
||||
if (state.hovered) {
|
||||
return palette.hoveredAccentColor;
|
||||
}
|
||||
|
||||
return palette.borderColor;
|
||||
}
|
||||
|
||||
inline float ResolveUIEditorPanelChromeBorderThickness(
|
||||
const UIEditorPanelChromeState& state,
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
return state.active
|
||||
? metrics.activeBorderThickness
|
||||
: metrics.inactiveBorderThickness;
|
||||
}
|
||||
|
||||
inline void AppendUIEditorPanelChromeBackground(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& panelRect,
|
||||
const UIEditorPanelChromeState& state,
|
||||
const UIEditorPanelChromePalette& palette = {},
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
drawList.AddFilledRect(panelRect, palette.surfaceColor, metrics.cornerRounding);
|
||||
drawList.AddRectOutline(
|
||||
panelRect,
|
||||
ResolveUIEditorPanelChromeBorderColor(state, palette),
|
||||
ResolveUIEditorPanelChromeBorderThickness(state, metrics),
|
||||
metrics.cornerRounding);
|
||||
drawList.AddFilledRect(
|
||||
BuildUIEditorPanelChromeHeaderRect(panelRect, metrics),
|
||||
palette.headerColor,
|
||||
metrics.cornerRounding);
|
||||
}
|
||||
|
||||
inline void AppendUIEditorPanelChromeForeground(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& panelRect,
|
||||
const UIEditorPanelChromeText& text,
|
||||
const UIEditorPanelChromePalette& palette = {},
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
if (!text.title.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(panelRect.x + metrics.titleInsetX, panelRect.y + metrics.titleInsetY),
|
||||
std::string(text.title),
|
||||
palette.textPrimary);
|
||||
}
|
||||
|
||||
if (!text.subtitle.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(panelRect.x + metrics.titleInsetX, panelRect.y + metrics.subtitleInsetY),
|
||||
std::string(text.subtitle),
|
||||
palette.textSecondary);
|
||||
}
|
||||
|
||||
if (!text.footer.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(panelRect.x + metrics.footerInsetX, panelRect.y + panelRect.height - metrics.footerInsetBottom),
|
||||
std::string(text.footer),
|
||||
palette.textMuted);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // 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,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);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<Schema name="EditorInspectorShell">
|
||||
<Element tag="InspectorPanel" allowUnknownAttributes="true">
|
||||
<Attribute name="vm" type="string" />
|
||||
<Attribute name="schema" type="document" documentKind="schema" />
|
||||
|
||||
<Element tag="Section" allowUnknownAttributes="true">
|
||||
<Attribute name="title" type="string" />
|
||||
<Attribute name="subtitle" type="string" />
|
||||
|
||||
<Element tag="Field" allowUnknownAttributes="true">
|
||||
<Attribute name="label" type="string" />
|
||||
<Attribute name="binding" type="string" />
|
||||
<Attribute
|
||||
name="widget"
|
||||
type="enum"
|
||||
values="Vector3Field,FloatField,EnumField,AssetField" />
|
||||
<Attribute name="step" type="number" />
|
||||
</Element>
|
||||
</Element>
|
||||
</Element>
|
||||
</Schema>
|
||||
@@ -1,30 +1,8 @@
|
||||
<View
|
||||
name="NewEditorShell"
|
||||
theme="../themes/editor_shell.xctheme">
|
||||
<Column padding="24" gap="16">
|
||||
<Card
|
||||
title="XCUI Core Validation"
|
||||
subtitle="Current batch: input state routing | minimal sandbox | native renderer"
|
||||
tone="accent"
|
||||
height="90">
|
||||
<Column gap="8">
|
||||
<Text text="这个试验面板只保留当前批次需要检查的控件。" />
|
||||
<Text text="无关的 editor 壳层面板暂时不放进来,避免干扰检查。" />
|
||||
</Column>
|
||||
</Card>
|
||||
|
||||
<Card title="Input Core" subtitle="hover focus active capture" height="196">
|
||||
<Column gap="12">
|
||||
<Text text="这一轮只需要检查下面这三个按钮。" />
|
||||
<Row gap="12">
|
||||
<Button id="input-hover" text="Hover / Focus" />
|
||||
<Button id="input-capture" text="Pointer Capture" capturePointer="true" />
|
||||
<Button id="input-route" text="Route Target" />
|
||||
</Row>
|
||||
<Text text="1. 鼠标移到左侧按钮:hover 应该变化,focus 应该保持空。" />
|
||||
<Text text="2. 按住中间按钮不放:focus、active、capture 都应该停在中间按钮。" />
|
||||
<Text text="3. 按住中间按钮拖到右侧再松开:hover 移到右侧,capture 清空,focus 仍留在中间。" />
|
||||
</Column>
|
||||
</Card>
|
||||
</Column>
|
||||
<Column
|
||||
id="editor-shell-root"
|
||||
width="fill"
|
||||
height="fill" />
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user