feat(new_editor): add standalone add-component utility window
This commit is contained in:
@@ -185,29 +185,6 @@ set(XCUI_EDITOR_HOST_RENDERING_SOURCES
|
||||
app/Rendering/D3D12/D3D12WindowRenderLoop.cpp
|
||||
)
|
||||
|
||||
add_library(XCUIEditorHost STATIC
|
||||
${XCUI_EDITOR_HOST_PLATFORM_SOURCES}
|
||||
${XCUI_EDITOR_HOST_RENDERING_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(XCUIEditorHost
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
)
|
||||
|
||||
xcui_editor_apply_common_target_settings(XCUIEditorHost PRIVATE)
|
||||
|
||||
target_link_libraries(XCUIEditorHost PRIVATE
|
||||
XCEngine
|
||||
d3d12.lib
|
||||
d3dcompiler.lib
|
||||
dwrite.lib
|
||||
dxgi.lib
|
||||
windowscodecs.lib
|
||||
)
|
||||
|
||||
if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
set(XCUI_EDITOR_APP_BOOTSTRAP_SOURCES
|
||||
app/Bootstrap/EditorApp.rc
|
||||
@@ -217,6 +194,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
|
||||
set(XCUI_EDITOR_APP_STATE_SOURCES
|
||||
app/State/EditorColorPickerToolState.cpp
|
||||
app/State/EditorUtilityWindowRequestState.cpp
|
||||
app/State/EditorSession.cpp
|
||||
)
|
||||
|
||||
@@ -241,6 +219,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
app/Features/ColorPicker/ColorPickerPanel.cpp
|
||||
app/Features/Hierarchy/HierarchyModel.cpp
|
||||
app/Features/Hierarchy/HierarchyPanel.cpp
|
||||
app/Features/Inspector/AddComponentPanel.cpp
|
||||
app/Features/Inspector/InspectorPanel.cpp
|
||||
app/Features/Inspector/InspectorPresentationModel.cpp
|
||||
app/Features/Inspector/InspectorSubject.cpp
|
||||
@@ -279,8 +258,10 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
|
||||
set(XCUI_EDITOR_APP_PLATFORM_SOURCES
|
||||
app/Platform/Win32/EditorWindow.cpp
|
||||
app/Platform/Win32/EditorAddComponentUtilityWindowContentController.cpp
|
||||
app/Platform/Win32/EditorColorPickerUtilityWindowContentController.cpp
|
||||
app/Platform/Win32/EditorFloatingWindowPlacement.cpp
|
||||
app/Platform/Win32/EditorStandaloneUtilityWindowContentController.cpp
|
||||
app/Platform/Win32/EditorWindowChromeController.cpp
|
||||
app/Platform/Win32/EditorWindowFrameDriver.cpp
|
||||
app/Platform/Win32/EditorWindowFrameOrchestrator.cpp
|
||||
@@ -306,56 +287,23 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
${XCUI_EDITOR_APP_SUPPORT_SOURCES}
|
||||
)
|
||||
|
||||
add_library(XCUIEditorAppCore STATIC
|
||||
set(XCUI_EDITOR_APP_INTERNAL_SOURCES
|
||||
${XCUI_EDITOR_HOST_PLATFORM_SOURCES}
|
||||
${XCUI_EDITOR_HOST_RENDERING_SOURCES}
|
||||
${XCUI_EDITOR_APP_CORE_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(XCUIEditorAppCore PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/engine/third_party/stb
|
||||
)
|
||||
|
||||
target_compile_definitions(XCUIEditorAppCore PRIVATE
|
||||
XCUIEDITOR_REPO_ROOT="${XCUIEDITOR_REPO_ROOT_PATH}"
|
||||
)
|
||||
|
||||
xcui_editor_apply_common_target_settings(XCUIEditorAppCore PRIVATE)
|
||||
|
||||
target_link_libraries(XCUIEditorAppCore PRIVATE
|
||||
XCUIEditorLib
|
||||
XCEngineRenderingEditorSupport
|
||||
)
|
||||
|
||||
add_library(XCUIEditorAppLib STATIC
|
||||
${XCUI_EDITOR_APP_PLATFORM_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(XCUIEditorAppLib PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
)
|
||||
|
||||
target_compile_definitions(XCUIEditorAppLib PRIVATE
|
||||
XCUIEDITOR_REPO_ROOT="${XCUIEDITOR_REPO_ROOT_PATH}"
|
||||
)
|
||||
|
||||
xcui_editor_apply_common_target_settings(XCUIEditorAppLib PRIVATE)
|
||||
|
||||
target_link_libraries(XCUIEditorAppLib PRIVATE
|
||||
XCUIEditorAppCore
|
||||
XCUIEditorLib
|
||||
XCUIEditorHost
|
||||
)
|
||||
|
||||
add_executable(XCUIEditorApp WIN32
|
||||
${XCUI_EDITOR_APP_BOOTSTRAP_SOURCES}
|
||||
${XCUI_EDITOR_APP_INTERNAL_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(XCUIEditorApp PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/engine/third_party/stb
|
||||
)
|
||||
|
||||
target_compile_definitions(XCUIEditorApp PRIVATE
|
||||
@@ -365,10 +313,9 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
xcui_editor_apply_common_target_settings(XCUIEditorApp PRIVATE)
|
||||
|
||||
target_link_libraries(XCUIEditorApp PRIVATE
|
||||
XCUIEditorAppLib
|
||||
XCUIEditorAppCore
|
||||
XCUIEditorLib
|
||||
XCUIEditorHost
|
||||
XCEngine
|
||||
XCEngineRenderingEditorSupport
|
||||
d3d12.lib
|
||||
d3dcompiler.lib
|
||||
dwrite.lib
|
||||
@@ -388,6 +335,13 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET XCUIEditorApp POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -f
|
||||
$<TARGET_FILE_DIR:XCUIEditorApp>/XCUIEditorAppCore.lib
|
||||
$<TARGET_FILE_DIR:XCUIEditorApp>/XCUIEditorAppLib.lib
|
||||
$<TARGET_FILE_DIR:XCUIEditorApp>/XCUIEditorHost.lib
|
||||
)
|
||||
|
||||
if(WIN32 AND XCENGINE_ENABLE_PHYSX)
|
||||
xcengine_copy_physx_runtime_dlls(XCUIEditorApp)
|
||||
endif()
|
||||
|
||||
@@ -71,6 +71,7 @@ bool EditorContext::Initialize(const std::filesystem::path& repoRoot) {
|
||||
AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize end");
|
||||
m_sceneRuntime.BindSelectionService(&m_selectionService);
|
||||
ResetEditorColorPickerToolState(m_colorPickerToolState);
|
||||
ResetEditorUtilityWindowRequestState(m_utilityWindowRequestState);
|
||||
SyncSessionFromSelectionService();
|
||||
m_hostCommandBridge.BindSession(m_session);
|
||||
m_hostCommandBridge.BindCommandFocusService(m_commandFocusService);
|
||||
@@ -166,6 +167,14 @@ const EditorColorPickerToolState& EditorContext::GetColorPickerToolState() const
|
||||
return m_colorPickerToolState;
|
||||
}
|
||||
|
||||
void EditorContext::RequestOpenUtilityWindow(EditorUtilityWindowKind kind) {
|
||||
RequestEditorUtilityWindow(m_utilityWindowRequestState, kind);
|
||||
}
|
||||
|
||||
std::optional<EditorUtilityWindowKind> EditorContext::ConsumeOpenUtilityWindowRequest() {
|
||||
return ConsumeEditorUtilityWindowRequest(m_utilityWindowRequestState);
|
||||
}
|
||||
|
||||
void EditorContext::SetSelection(EditorSelectionState selection) {
|
||||
m_selectionService.SetSelection(std::move(selection));
|
||||
SyncSessionFromSelectionService();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "State/EditorCommandFocusService.h"
|
||||
#include "State/EditorSelectionService.h"
|
||||
#include "State/EditorSession.h"
|
||||
#include "State/EditorUtilityWindowRequestState.h"
|
||||
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
|
||||
#include <XCEditor/Shell/UIEditorShellAsset.h>
|
||||
#include <XCEditor/Shell/UIEditorShellInteraction.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -52,6 +54,8 @@ public:
|
||||
const EditorSceneRuntime& GetSceneRuntime() const;
|
||||
EditorColorPickerToolState& GetColorPickerToolState();
|
||||
const EditorColorPickerToolState& GetColorPickerToolState() const;
|
||||
void RequestOpenUtilityWindow(EditorUtilityWindowKind kind);
|
||||
std::optional<EditorUtilityWindowKind> ConsumeOpenUtilityWindowRequest();
|
||||
void SetSelection(EditorSelectionState selection);
|
||||
void ClearSelection();
|
||||
void SyncSessionFromSelectionService();
|
||||
@@ -89,6 +93,7 @@ private:
|
||||
EditorProjectRuntime m_projectRuntime = {};
|
||||
EditorSceneRuntime m_sceneRuntime = {};
|
||||
EditorColorPickerToolState m_colorPickerToolState = {};
|
||||
EditorUtilityWindowRequestState m_utilityWindowRequestState = {};
|
||||
EditorHostCommandBridge m_hostCommandBridge = {};
|
||||
Ports::SystemInteractionPort* m_systemInteractionHost = nullptr;
|
||||
std::string m_lastStatus = {};
|
||||
|
||||
304
new_editor/app/Features/Inspector/AddComponentPanel.cpp
Normal file
304
new_editor/app/Features/Inspector/AddComponentPanel.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
#include "Features/Inspector/AddComponentPanel.h"
|
||||
|
||||
#include "Composition/EditorContext.h"
|
||||
#include "Features/Inspector/Components/IInspectorComponentEditor.h"
|
||||
#include "Features/Inspector/Components/InspectorComponentEditorRegistry.h"
|
||||
|
||||
#include <XCEditor/Fields/UIEditorFieldStyle.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::UI::UIColor;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIPointerButton;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
|
||||
constexpr float kPanelPadding = 8.0f;
|
||||
constexpr float kHeaderGap = 8.0f;
|
||||
constexpr float kCaptionHeight = 16.0f;
|
||||
constexpr float kTargetHeight = 16.0f;
|
||||
constexpr float kCaptionFontSize = 11.0f;
|
||||
constexpr float kTargetFontSize = 12.0f;
|
||||
constexpr float kEntryHeight = 30.0f;
|
||||
constexpr float kEntryGap = 4.0f;
|
||||
constexpr float kEntryTextPadding = 8.0f;
|
||||
constexpr float kReasonFontSize = 10.0f;
|
||||
constexpr std::size_t kInvalidEntryIndex = static_cast<std::size_t>(-1);
|
||||
|
||||
bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
|
||||
return point.x >= rect.x &&
|
||||
point.x <= rect.x + rect.width &&
|
||||
point.y >= rect.y &&
|
||||
point.y <= rect.y + rect.height;
|
||||
}
|
||||
|
||||
float ResolveTextTop(float rectY, float rectHeight, float fontSize) {
|
||||
const float lineHeight = fontSize * 1.6f;
|
||||
return rectY + std::floor((rectHeight - lineHeight) * 0.5f);
|
||||
}
|
||||
|
||||
UIColor ResolveEntryFillColor(
|
||||
bool enabled,
|
||||
bool hovered,
|
||||
bool pressed,
|
||||
const Widgets::UIEditorPropertyGridPalette& palette) {
|
||||
if (!enabled) {
|
||||
return palette.valueBoxReadOnlyColor;
|
||||
}
|
||||
|
||||
if (pressed) {
|
||||
return palette.valueBoxEditingColor;
|
||||
}
|
||||
|
||||
if (hovered) {
|
||||
return palette.valueBoxHoverColor;
|
||||
}
|
||||
|
||||
return palette.valueBoxColor;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void AddComponentPanel::ResetPanelState() {
|
||||
m_visible = false;
|
||||
m_hasTarget = false;
|
||||
m_bounds = {};
|
||||
m_targetDisplayName.clear();
|
||||
m_entries.clear();
|
||||
ResetInteractionState();
|
||||
}
|
||||
|
||||
void AddComponentPanel::ResetInteractionState() {
|
||||
m_hoveredEntryIndex = kInvalidEntryIndex;
|
||||
m_pressedEntryIndex = kInvalidEntryIndex;
|
||||
}
|
||||
|
||||
void AddComponentPanel::RebuildEntries(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
const auto& editors = InspectorComponentEditorRegistry::Get().GetEditors();
|
||||
m_entries.clear();
|
||||
m_entries.reserve(editors.size());
|
||||
|
||||
const float entryX = m_bounds.x + kPanelPadding;
|
||||
const float entryWidth = (std::max)(m_bounds.width - kPanelPadding * 2.0f, 0.0f);
|
||||
float nextY =
|
||||
m_bounds.y +
|
||||
kPanelPadding +
|
||||
kCaptionHeight +
|
||||
kTargetHeight +
|
||||
kHeaderGap;
|
||||
|
||||
for (const auto& editorPtr : editors) {
|
||||
const IInspectorComponentEditor* editor = editorPtr.get();
|
||||
if (editor == nullptr || !editor->ShowInAddComponentMenu()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EntryPresentation entry = {};
|
||||
entry.componentTypeName = std::string(editor->GetComponentTypeName());
|
||||
entry.displayName = std::string(editor->GetDisplayName());
|
||||
entry.enabled = editor->CanAddTo(gameObject);
|
||||
entry.disabledReason = std::string(editor->GetAddDisabledReason(gameObject));
|
||||
entry.rect = UIRect(entryX, nextY, entryWidth, kEntryHeight);
|
||||
m_entries.push_back(std::move(entry));
|
||||
nextY += kEntryHeight + kEntryGap;
|
||||
}
|
||||
|
||||
if (m_hoveredEntryIndex >= m_entries.size()) {
|
||||
m_hoveredEntryIndex = kInvalidEntryIndex;
|
||||
}
|
||||
if (m_pressedEntryIndex >= m_entries.size()) {
|
||||
m_pressedEntryIndex = kInvalidEntryIndex;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddComponentPanel::TryActivateEntry(
|
||||
EditorContext& context,
|
||||
std::size_t entryIndex) {
|
||||
if (entryIndex >= m_entries.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EntryPresentation& entry = m_entries[entryIndex];
|
||||
if (!entry.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!context.GetSceneRuntime().AddComponentToSelectedGameObject(entry.componentTypeName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hasTarget = context.GetSceneRuntime().GetSelectedGameObject() != nullptr;
|
||||
m_targetDisplayName = context.GetSceneRuntime().GetSelectedDisplayName();
|
||||
RebuildEntries(context.GetSceneRuntime().GetSelectedGameObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t AddComponentPanel::HitTestEntry(const UIPoint& point) const {
|
||||
for (std::size_t index = 0u; index < m_entries.size(); ++index) {
|
||||
if (ContainsPoint(m_entries[index].rect, point)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return kInvalidEntryIndex;
|
||||
}
|
||||
|
||||
void AddComponentPanel::Update(
|
||||
EditorContext& context,
|
||||
const AddComponentPanelHostContext& hostContext,
|
||||
const std::vector<UIInputEvent>& inputEvents) {
|
||||
if (!hostContext.mounted) {
|
||||
ResetPanelState();
|
||||
return;
|
||||
}
|
||||
|
||||
m_visible = true;
|
||||
m_bounds = hostContext.bounds;
|
||||
m_hasTarget = context.GetSceneRuntime().GetSelectedGameObject() != nullptr;
|
||||
m_targetDisplayName = context.GetSceneRuntime().GetSelectedDisplayName();
|
||||
RebuildEntries(context.GetSceneRuntime().GetSelectedGameObject());
|
||||
|
||||
if (hostContext.focusLost) {
|
||||
ResetInteractionState();
|
||||
}
|
||||
|
||||
for (const UIInputEvent& event : inputEvents) {
|
||||
switch (event.type) {
|
||||
case UIInputEventType::PointerMove:
|
||||
m_hoveredEntryIndex = HitTestEntry(event.position);
|
||||
break;
|
||||
|
||||
case UIInputEventType::PointerLeave:
|
||||
case UIInputEventType::FocusLost:
|
||||
ResetInteractionState();
|
||||
break;
|
||||
|
||||
case UIInputEventType::PointerButtonDown:
|
||||
if (event.pointerButton != UIPointerButton::Left) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_hoveredEntryIndex = HitTestEntry(event.position);
|
||||
m_pressedEntryIndex =
|
||||
m_hoveredEntryIndex < m_entries.size() &&
|
||||
m_entries[m_hoveredEntryIndex].enabled
|
||||
? m_hoveredEntryIndex
|
||||
: kInvalidEntryIndex;
|
||||
break;
|
||||
|
||||
case UIInputEventType::PointerButtonUp:
|
||||
if (event.pointerButton != UIPointerButton::Left) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_hoveredEntryIndex = HitTestEntry(event.position);
|
||||
if (m_pressedEntryIndex != kInvalidEntryIndex &&
|
||||
m_pressedEntryIndex == m_hoveredEntryIndex) {
|
||||
TryActivateEntry(context, m_pressedEntryIndex);
|
||||
}
|
||||
m_pressedEntryIndex = kInvalidEntryIndex;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddComponentPanel::Append(UIDrawList& drawList) const {
|
||||
if (!m_visible || m_bounds.width <= 0.0f || m_bounds.height <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorPropertyGridPalette& palette =
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridPalette();
|
||||
const Widgets::UIEditorPropertyGridMetrics& metrics =
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridMetrics();
|
||||
|
||||
drawList.AddFilledRect(m_bounds, palette.surfaceColor);
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
m_bounds.x + kPanelPadding,
|
||||
ResolveTextTop(m_bounds.y + kPanelPadding, kCaptionHeight, kCaptionFontSize)),
|
||||
"Target",
|
||||
palette.labelTextColor,
|
||||
kCaptionFontSize);
|
||||
|
||||
const std::string targetText = m_hasTarget
|
||||
? m_targetDisplayName
|
||||
: std::string("No scene object selected.");
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
m_bounds.x + kPanelPadding,
|
||||
ResolveTextTop(
|
||||
m_bounds.y + kPanelPadding + kCaptionHeight,
|
||||
kTargetHeight,
|
||||
kTargetFontSize)),
|
||||
targetText,
|
||||
m_hasTarget ? palette.valueTextColor : palette.readOnlyValueTextColor,
|
||||
kTargetFontSize);
|
||||
|
||||
if (m_entries.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
m_bounds.x + kPanelPadding,
|
||||
ResolveTextTop(
|
||||
m_bounds.y + kPanelPadding + kCaptionHeight + kTargetHeight + kHeaderGap,
|
||||
kTargetHeight,
|
||||
kTargetFontSize)),
|
||||
"No registered component editors.",
|
||||
palette.readOnlyValueTextColor,
|
||||
kTargetFontSize);
|
||||
return;
|
||||
}
|
||||
|
||||
drawList.PushClipRect(m_bounds);
|
||||
for (std::size_t index = 0u; index < m_entries.size(); ++index) {
|
||||
const EntryPresentation& entry = m_entries[index];
|
||||
const bool hovered = index == m_hoveredEntryIndex;
|
||||
const bool pressed = index == m_pressedEntryIndex;
|
||||
drawList.AddFilledRect(
|
||||
entry.rect,
|
||||
ResolveEntryFillColor(entry.enabled, hovered, pressed, palette),
|
||||
metrics.valueBoxRounding);
|
||||
drawList.AddRectOutline(
|
||||
entry.rect,
|
||||
palette.valueBoxBorderColor,
|
||||
metrics.borderThickness,
|
||||
metrics.valueBoxRounding);
|
||||
|
||||
const UIColor nameColor =
|
||||
entry.enabled ? palette.valueTextColor : palette.readOnlyValueTextColor;
|
||||
const float nameY = entry.disabledReason.empty()
|
||||
? ResolveTextTop(entry.rect.y, entry.rect.height, kTargetFontSize)
|
||||
: entry.rect.y + 2.0f;
|
||||
drawList.AddText(
|
||||
UIPoint(entry.rect.x + kEntryTextPadding, nameY),
|
||||
entry.displayName,
|
||||
nameColor,
|
||||
kTargetFontSize);
|
||||
|
||||
if (!entry.disabledReason.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
entry.rect.x + kEntryTextPadding,
|
||||
entry.rect.y + entry.rect.height - 14.0f),
|
||||
entry.disabledReason,
|
||||
palette.labelTextColor,
|
||||
kReasonFontSize);
|
||||
}
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
57
new_editor/app/Features/Inspector/AddComponentPanel.h
Normal file
57
new_editor/app/Features/Inspector/AddComponentPanel.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine::Components {
|
||||
class GameObject;
|
||||
}
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorContext;
|
||||
|
||||
struct AddComponentPanelHostContext {
|
||||
bool mounted = false;
|
||||
::XCEngine::UI::UIRect bounds = {};
|
||||
bool focused = false;
|
||||
bool focusGained = false;
|
||||
bool focusLost = false;
|
||||
};
|
||||
|
||||
class AddComponentPanel {
|
||||
public:
|
||||
void ResetInteractionState();
|
||||
void Update(
|
||||
EditorContext& context,
|
||||
const AddComponentPanelHostContext& hostContext,
|
||||
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents);
|
||||
void Append(::XCEngine::UI::UIDrawList& drawList) const;
|
||||
|
||||
private:
|
||||
struct EntryPresentation {
|
||||
std::string componentTypeName = {};
|
||||
std::string displayName = {};
|
||||
std::string disabledReason = {};
|
||||
::XCEngine::UI::UIRect rect = {};
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
void ResetPanelState();
|
||||
void RebuildEntries(const ::XCEngine::Components::GameObject* gameObject);
|
||||
bool TryActivateEntry(EditorContext& context, std::size_t entryIndex);
|
||||
std::size_t HitTestEntry(const ::XCEngine::UI::UIPoint& point) const;
|
||||
|
||||
bool m_visible = false;
|
||||
bool m_hasTarget = false;
|
||||
::XCEngine::UI::UIRect m_bounds = {};
|
||||
std::string m_targetDisplayName = {};
|
||||
std::vector<EntryPresentation> m_entries = {};
|
||||
std::size_t m_hoveredEntryIndex = static_cast<std::size_t>(-1);
|
||||
std::size_t m_pressedEntryIndex = static_cast<std::size_t>(-1);
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -16,6 +16,18 @@ public:
|
||||
return "Audio Listener";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::AudioListenerComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::AudioListenerComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -17,6 +17,16 @@ public:
|
||||
return "Audio Source";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddMultipleInspectorComponentsToGameObject(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetInvalidInspectorAddDisabledReason(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -16,6 +16,16 @@ public:
|
||||
return "Box Collider";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddSingleColliderInspectorComponentToGameObject(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetSingleColliderInspectorAddDisabledReason(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -16,6 +16,18 @@ public:
|
||||
return "Camera";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::CameraComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::CameraComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -16,6 +16,16 @@ public:
|
||||
return "Capsule Collider";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddSingleColliderInspectorComponentToGameObject(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetSingleColliderInspectorAddDisabledReason(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -45,6 +45,22 @@ public:
|
||||
const InspectorComponentEditorContext& context,
|
||||
const Widgets::UIEditorPropertyGridField& field) const = 0;
|
||||
|
||||
virtual bool ShowInAddComponentMenu() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const {
|
||||
(void)gameObject;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const {
|
||||
(void)gameObject;
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual void AppendStructureSignature(
|
||||
const InspectorComponentEditorContext& context,
|
||||
std::string& signature) const {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
|
||||
|
||||
#include <XCEngine/Components/ColliderComponent.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
|
||||
@@ -310,6 +312,66 @@ inline void AppendInspectorStructureToken(
|
||||
signature.push_back('|');
|
||||
}
|
||||
|
||||
inline bool CanAddMultipleInspectorComponentsToGameObject(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
return gameObject != nullptr;
|
||||
}
|
||||
|
||||
inline std::string_view GetInvalidInspectorAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
return gameObject == nullptr
|
||||
? std::string_view("Invalid")
|
||||
: std::string_view{};
|
||||
}
|
||||
|
||||
inline bool CanAddBuiltInInspectorComponentToGameObject(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
(void)gameObject;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string_view GetBuiltInInspectorAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
(void)gameObject;
|
||||
return "Built-in";
|
||||
}
|
||||
|
||||
template <typename TComponent>
|
||||
bool CanAddUniqueInspectorComponentToGameObject(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
return gameObject != nullptr &&
|
||||
gameObject->GetComponent<TComponent>() == nullptr;
|
||||
}
|
||||
|
||||
template <typename TComponent>
|
||||
std::string_view GetUniqueInspectorComponentAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
if (gameObject == nullptr) {
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
return gameObject->GetComponent<TComponent>() != nullptr
|
||||
? std::string_view("Already Added")
|
||||
: std::string_view{};
|
||||
}
|
||||
|
||||
inline bool CanAddSingleColliderInspectorComponentToGameObject(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
return gameObject != nullptr &&
|
||||
gameObject->GetComponent<::XCEngine::Components::ColliderComponent>() == nullptr;
|
||||
}
|
||||
|
||||
inline std::string_view GetSingleColliderInspectorAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) {
|
||||
if (gameObject == nullptr) {
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
return gameObject->GetComponent<::XCEngine::Components::ColliderComponent>() != nullptr
|
||||
? std::string_view("Only One Collider Supported")
|
||||
: std::string_view{};
|
||||
}
|
||||
|
||||
template <typename TComponent>
|
||||
const TComponent* ResolveInspectorComponent(
|
||||
const InspectorComponentEditorContext& context) {
|
||||
|
||||
@@ -16,6 +16,18 @@ public:
|
||||
return "Light";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::LightComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::LightComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -16,6 +16,18 @@ public:
|
||||
return "Mesh Filter";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::MeshFilterComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::MeshFilterComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -20,6 +20,18 @@ public:
|
||||
return "Mesh Renderer";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::MeshRendererComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::MeshRendererComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -16,6 +16,18 @@ public:
|
||||
return "Rigidbody";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::RigidbodyComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::RigidbodyComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -16,6 +16,16 @@ public:
|
||||
return "Sphere Collider";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddSingleColliderInspectorComponentToGameObject(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetSingleColliderInspectorAddDisabledReason(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -12,6 +12,16 @@ std::string_view TransformInspectorComponentEditor::GetDisplayName() const {
|
||||
return "Transform";
|
||||
}
|
||||
|
||||
bool TransformInspectorComponentEditor::CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const {
|
||||
return CanAddBuiltInInspectorComponentToGameObject(gameObject);
|
||||
}
|
||||
|
||||
std::string_view TransformInspectorComponentEditor::GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const {
|
||||
return GetBuiltInInspectorAddDisabledReason(gameObject);
|
||||
}
|
||||
|
||||
void TransformInspectorComponentEditor::BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
std::vector<InspectorSectionBinding>& outSections) const {
|
||||
|
||||
@@ -9,6 +9,10 @@ public:
|
||||
std::string_view GetComponentTypeName() const override;
|
||||
std::string_view GetDisplayName() const override;
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override;
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override;
|
||||
bool CanRemove(
|
||||
const InspectorComponentEditorContext& context) const override;
|
||||
|
||||
|
||||
@@ -16,6 +16,18 @@ public:
|
||||
return "Volume Renderer";
|
||||
}
|
||||
|
||||
bool CanAddTo(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return CanAddUniqueInspectorComponentToGameObject<
|
||||
::XCEngine::Components::VolumeRendererComponent>(gameObject);
|
||||
}
|
||||
|
||||
std::string_view GetAddDisabledReason(
|
||||
const ::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return GetUniqueInspectorComponentAddDisabledReason<
|
||||
::XCEngine::Components::VolumeRendererComponent>(gameObject);
|
||||
}
|
||||
|
||||
protected:
|
||||
void BuildBindingSections(
|
||||
const InspectorComponentEditorContext& context,
|
||||
|
||||
@@ -22,7 +22,9 @@ using ::XCEngine::UI::UIColor;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIPointerButton;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
|
||||
constexpr float kPanelPadding = 10.0f;
|
||||
constexpr float kTitleHeight = 18.0f;
|
||||
@@ -30,6 +32,9 @@ constexpr float kSubtitleHeight = 16.0f;
|
||||
constexpr float kHeaderGap = 10.0f;
|
||||
constexpr float kTitleFontSize = 13.0f;
|
||||
constexpr float kSubtitleFontSize = 11.0f;
|
||||
constexpr float kAddComponentButtonHeight = 24.0f;
|
||||
constexpr float kAddComponentButtonTopGap = 10.0f;
|
||||
constexpr float kAddComponentButtonFontSize = 12.0f;
|
||||
constexpr UIColor kTitleColor(0.930f, 0.930f, 0.930f, 1.0f);
|
||||
constexpr UIColor kSubtitleColor(0.660f, 0.660f, 0.660f, 1.0f);
|
||||
constexpr UIColor kSurfaceColor(0.10f, 0.10f, 0.10f, 1.0f);
|
||||
@@ -68,6 +73,27 @@ float ResolveTextTop(float rectY, float rectHeight, float fontSize) {
|
||||
return rectY + std::floor((rectHeight - lineHeight) * 0.5f);
|
||||
}
|
||||
|
||||
float ResolveCenteredTextX(
|
||||
const UIRect& rect,
|
||||
std::string_view text,
|
||||
float fontSize) {
|
||||
const float estimatedTextWidth =
|
||||
static_cast<float>(text.size()) * fontSize * 0.56f;
|
||||
return rect.x + std::floor((std::max)(rect.width - estimatedTextWidth, 0.0f) * 0.5f);
|
||||
}
|
||||
|
||||
UIColor ResolveAddComponentButtonFillColor(bool hovered, bool pressed) {
|
||||
const Widgets::UIEditorPropertyGridPalette& palette =
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridPalette();
|
||||
if (pressed) {
|
||||
return palette.valueBoxEditingColor;
|
||||
}
|
||||
if (hovered) {
|
||||
return palette.valueBoxHoverColor;
|
||||
}
|
||||
return palette.valueBoxColor;
|
||||
}
|
||||
|
||||
UIEditorHostCommandEvaluationResult BuildEvaluationResult(
|
||||
bool executable,
|
||||
std::string message) {
|
||||
@@ -115,6 +141,7 @@ void InspectorPanel::ResetInteractionState() {
|
||||
m_interactionState = {};
|
||||
m_gridFrame = {};
|
||||
m_lastAppliedColorPickerRevision = 0u;
|
||||
ResetAddComponentButtonState();
|
||||
}
|
||||
|
||||
void InspectorPanel::SyncExpansionState(bool subjectChanged) {
|
||||
@@ -182,6 +209,10 @@ float InspectorPanel::ResolveHeaderHeight() const {
|
||||
return kTitleHeight + kSubtitleHeight + kHeaderGap;
|
||||
}
|
||||
|
||||
bool InspectorPanel::ShouldShowAddComponentButton() const {
|
||||
return m_subject.kind == InspectorSubjectKind::SceneObject;
|
||||
}
|
||||
|
||||
UIRect InspectorPanel::BuildGridBounds() const {
|
||||
const float horizontalPadding =
|
||||
ResolveInspectorHorizontalPadding(m_presentation);
|
||||
@@ -196,6 +227,33 @@ UIRect InspectorPanel::BuildGridBounds() const {
|
||||
return UIRect(x, y, width, height);
|
||||
}
|
||||
|
||||
UIRect InspectorPanel::BuildAddComponentButtonRect() const {
|
||||
if (!ShouldShowAddComponentButton()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const float horizontalPadding =
|
||||
ResolveInspectorHorizontalPadding(m_presentation);
|
||||
const float bottomPadding = ResolveInspectorBottomPadding(m_presentation);
|
||||
float buttonTop = BuildGridBounds().y;
|
||||
if (!m_gridFrame.layout.fieldRowRects.empty()) {
|
||||
const UIRect& lastFieldRect = m_gridFrame.layout.fieldRowRects.back();
|
||||
buttonTop = lastFieldRect.y + lastFieldRect.height + kAddComponentButtonTopGap;
|
||||
} else if (!m_gridFrame.layout.sectionHeaderRects.empty()) {
|
||||
const UIRect& lastSectionRect = m_gridFrame.layout.sectionHeaderRects.back();
|
||||
buttonTop = lastSectionRect.y + lastSectionRect.height + kAddComponentButtonTopGap;
|
||||
}
|
||||
|
||||
const float maxVisibleTop =
|
||||
m_bounds.y + m_bounds.height - bottomPadding - kAddComponentButtonHeight;
|
||||
buttonTop = (std::min)(buttonTop, maxVisibleTop);
|
||||
return UIRect(
|
||||
m_bounds.x + horizontalPadding,
|
||||
buttonTop,
|
||||
(std::max)(m_bounds.width - horizontalPadding * 2.0f, 0.0f),
|
||||
kAddComponentButtonHeight);
|
||||
}
|
||||
|
||||
const InspectorPresentationComponentBinding* InspectorPanel::FindSelectedComponentBinding() const {
|
||||
if (!m_fieldSelection.HasSelection()) {
|
||||
return nullptr;
|
||||
@@ -399,6 +457,66 @@ void InspectorPanel::RequestColorPicker(
|
||||
field->fieldId,
|
||||
field->colorValue.value,
|
||||
field->colorValue.showAlpha);
|
||||
context.RequestOpenUtilityWindow(EditorUtilityWindowKind::ColorPicker);
|
||||
}
|
||||
|
||||
void InspectorPanel::ResetAddComponentButtonState() {
|
||||
m_addComponentButtonHovered = false;
|
||||
m_addComponentButtonPressed = false;
|
||||
}
|
||||
|
||||
void InspectorPanel::UpdateAddComponentButton(
|
||||
EditorContext& context,
|
||||
const std::vector<UIInputEvent>& inputEvents) {
|
||||
if (!ShouldShowAddComponentButton()) {
|
||||
ResetAddComponentButtonState();
|
||||
return;
|
||||
}
|
||||
|
||||
const UIRect buttonRect = BuildAddComponentButtonRect();
|
||||
if (buttonRect.width <= 0.0f || buttonRect.height <= 0.0f) {
|
||||
ResetAddComponentButtonState();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const UIInputEvent& event : inputEvents) {
|
||||
switch (event.type) {
|
||||
case UIInputEventType::PointerMove:
|
||||
m_addComponentButtonHovered = ContainsPoint(buttonRect, event.position);
|
||||
break;
|
||||
|
||||
case UIInputEventType::PointerLeave:
|
||||
case UIInputEventType::FocusLost:
|
||||
ResetAddComponentButtonState();
|
||||
break;
|
||||
|
||||
case UIInputEventType::PointerButtonDown:
|
||||
if (event.pointerButton != UIPointerButton::Left) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_addComponentButtonHovered = ContainsPoint(buttonRect, event.position);
|
||||
m_addComponentButtonPressed = m_addComponentButtonHovered;
|
||||
break;
|
||||
|
||||
case UIInputEventType::PointerButtonUp:
|
||||
if (event.pointerButton != UIPointerButton::Left) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_addComponentButtonPressed &&
|
||||
ContainsPoint(buttonRect, event.position)) {
|
||||
context.RequestOpenUtilityWindow(EditorUtilityWindowKind::AddComponent);
|
||||
}
|
||||
|
||||
m_addComponentButtonHovered = ContainsPoint(buttonRect, event.position);
|
||||
m_addComponentButtonPressed = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InspectorPanel::ApplyChangedField(std::string_view fieldId) {
|
||||
@@ -479,11 +597,6 @@ void InspectorPanel::Update(
|
||||
RefreshPresentation(context, subjectChanged);
|
||||
ApplyColorPickerToolValue(context);
|
||||
|
||||
if (m_presentation.sections.empty()) {
|
||||
m_gridFrame = {};
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<UIInputEvent> filteredEvents =
|
||||
BuildUIEditorPanelInputEvents(
|
||||
m_bounds,
|
||||
@@ -507,28 +620,32 @@ void InspectorPanel::Update(
|
||||
filteredEvents,
|
||||
m_bounds,
|
||||
dispatchEntry.allowInteraction);
|
||||
m_gridFrame = UpdateUIEditorPropertyGridInteraction(
|
||||
m_interactionState,
|
||||
m_fieldSelection,
|
||||
m_sectionExpansion,
|
||||
m_propertyEditModel,
|
||||
BuildGridBounds(),
|
||||
m_presentation.sections,
|
||||
filteredEvents,
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridMetrics());
|
||||
if (m_gridFrame.result.pickerRequested &&
|
||||
!m_gridFrame.result.requestedFieldId.empty()) {
|
||||
RequestColorPicker(context, m_gridFrame.result.requestedFieldId);
|
||||
}
|
||||
m_gridFrame = {};
|
||||
if (!m_presentation.sections.empty()) {
|
||||
m_gridFrame = UpdateUIEditorPropertyGridInteraction(
|
||||
m_interactionState,
|
||||
m_fieldSelection,
|
||||
m_sectionExpansion,
|
||||
m_propertyEditModel,
|
||||
BuildGridBounds(),
|
||||
m_presentation.sections,
|
||||
filteredEvents,
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridMetrics());
|
||||
if (m_gridFrame.result.pickerRequested &&
|
||||
!m_gridFrame.result.requestedFieldId.empty()) {
|
||||
RequestColorPicker(context, m_gridFrame.result.requestedFieldId);
|
||||
}
|
||||
|
||||
if (m_gridFrame.result.fieldValueChanged &&
|
||||
!m_gridFrame.result.changedFieldId.empty()) {
|
||||
if (ApplyChangedField(m_gridFrame.result.changedFieldId)) {
|
||||
RefreshPresentation(context, false);
|
||||
} else {
|
||||
ForceResyncPresentation(context);
|
||||
if (m_gridFrame.result.fieldValueChanged &&
|
||||
!m_gridFrame.result.changedFieldId.empty()) {
|
||||
if (ApplyChangedField(m_gridFrame.result.changedFieldId)) {
|
||||
RefreshPresentation(context, false);
|
||||
} else {
|
||||
ForceResyncPresentation(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateAddComponentButton(context, filteredEvents);
|
||||
}
|
||||
|
||||
void InspectorPanel::Append(UIDrawList& drawList) const {
|
||||
@@ -565,20 +682,50 @@ void InspectorPanel::Append(UIDrawList& drawList) const {
|
||||
kSubtitleFontSize);
|
||||
}
|
||||
|
||||
if (m_presentation.sections.empty()) {
|
||||
if (!m_presentation.sections.empty()) {
|
||||
Widgets::AppendUIEditorPropertyGrid(
|
||||
drawList,
|
||||
BuildGridBounds(),
|
||||
m_presentation.sections,
|
||||
m_fieldSelection,
|
||||
m_sectionExpansion,
|
||||
m_propertyEditModel,
|
||||
m_interactionState.propertyGridState,
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridPalette(),
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridMetrics());
|
||||
}
|
||||
|
||||
if (!ShouldShowAddComponentButton()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Widgets::AppendUIEditorPropertyGrid(
|
||||
drawList,
|
||||
BuildGridBounds(),
|
||||
m_presentation.sections,
|
||||
m_fieldSelection,
|
||||
m_sectionExpansion,
|
||||
m_propertyEditModel,
|
||||
m_interactionState.propertyGridState,
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridPalette(),
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridMetrics());
|
||||
const UIRect buttonRect = BuildAddComponentButtonRect();
|
||||
if (buttonRect.width <= 0.0f || buttonRect.height <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Widgets::UIEditorPropertyGridPalette& palette =
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridPalette();
|
||||
const Widgets::UIEditorPropertyGridMetrics& metrics =
|
||||
::XCEngine::UI::Editor::GetUIEditorFixedPropertyGridMetrics();
|
||||
drawList.AddFilledRect(
|
||||
buttonRect,
|
||||
ResolveAddComponentButtonFillColor(
|
||||
m_addComponentButtonHovered,
|
||||
m_addComponentButtonPressed),
|
||||
metrics.valueBoxRounding);
|
||||
drawList.AddRectOutline(
|
||||
buttonRect,
|
||||
palette.valueBoxBorderColor,
|
||||
metrics.borderThickness,
|
||||
metrics.valueBoxRounding);
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
ResolveCenteredTextX(buttonRect, "Add Component", kAddComponentButtonFontSize),
|
||||
ResolveTextTop(buttonRect.y, buttonRect.height, kAddComponentButtonFontSize)),
|
||||
"Add Component",
|
||||
palette.valueTextColor,
|
||||
kAddComponentButtonFontSize);
|
||||
}
|
||||
|
||||
UIEditorHostCommandEvaluationResult InspectorPanel::EvaluateEditCommand(
|
||||
|
||||
@@ -44,7 +44,9 @@ private:
|
||||
void SyncSelectionState();
|
||||
std::string BuildSubjectKey() const;
|
||||
float ResolveHeaderHeight() const;
|
||||
bool ShouldShowAddComponentButton() const;
|
||||
::XCEngine::UI::UIRect BuildGridBounds() const;
|
||||
::XCEngine::UI::UIRect BuildAddComponentButtonRect() const;
|
||||
const InspectorPresentationComponentBinding* FindSelectedComponentBinding() const;
|
||||
const Widgets::UIEditorPropertyGridField* FindField(std::string_view fieldId) const;
|
||||
Widgets::UIEditorPropertyGridField* FindMutableField(std::string_view fieldId);
|
||||
@@ -54,6 +56,10 @@ private:
|
||||
void ForceResyncPresentation(EditorContext& context);
|
||||
bool ApplyColorPickerToolValue(EditorContext& context);
|
||||
void RequestColorPicker(EditorContext& context, std::string_view fieldId);
|
||||
void ResetAddComponentButtonState();
|
||||
void UpdateAddComponentButton(
|
||||
EditorContext& context,
|
||||
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents);
|
||||
bool ApplyChangedField(std::string_view fieldId);
|
||||
|
||||
EditorCommandFocusService* m_commandFocusService = nullptr;
|
||||
@@ -73,6 +79,8 @@ private:
|
||||
std::uint64_t m_lastProjectSelectionStamp = 0u;
|
||||
std::uint64_t m_lastSceneInspectorRevision = 0u;
|
||||
std::uint64_t m_lastAppliedColorPickerRevision = 0u;
|
||||
bool m_addComponentButtonHovered = false;
|
||||
bool m_addComponentButtonPressed = false;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "Platform/Win32/EditorAddComponentUtilityWindowContentController.h"
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
EditorAddComponentUtilityWindowContentController::
|
||||
EditorAddComponentUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize)
|
||||
: EditorStandaloneUtilityWindowContentController(minimumOuterSize) {}
|
||||
|
||||
EditorAddComponentUtilityWindowContentController::
|
||||
~EditorAddComponentUtilityWindowContentController() = default;
|
||||
|
||||
void EditorAddComponentUtilityWindowContentController::OnShutdown() {
|
||||
m_addComponentPanel.ResetInteractionState();
|
||||
}
|
||||
|
||||
void EditorAddComponentUtilityWindowContentController::OnResetInteractionState() {
|
||||
m_addComponentPanel.ResetInteractionState();
|
||||
}
|
||||
|
||||
EditorWindowFrameTransferRequests
|
||||
EditorAddComponentUtilityWindowContentController::UpdateStandaloneContent(
|
||||
const EditorStandaloneUtilityWindowFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) {
|
||||
m_addComponentPanel.Update(
|
||||
context.editorContext,
|
||||
AddComponentPanelHostContext{
|
||||
.mounted = true,
|
||||
.bounds = context.bounds,
|
||||
.focused = context.focused,
|
||||
.focusGained = context.focusGained,
|
||||
.focusLost = context.focusLost,
|
||||
},
|
||||
context.inputEvents);
|
||||
|
||||
::XCEngine::UI::UIDrawList& drawList =
|
||||
drawData.EmplaceDrawList("XCEditorUtility.AddComponent");
|
||||
m_addComponentPanel.Append(drawList);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<EditorWindowContentController>
|
||||
CreateEditorAddComponentUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize) {
|
||||
return std::make_unique<EditorAddComponentUtilityWindowContentController>(
|
||||
minimumOuterSize);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "Features/Inspector/AddComponentPanel.h"
|
||||
#include "Platform/Win32/EditorStandaloneUtilityWindowContentController.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorAddComponentUtilityWindowContentController final
|
||||
: public EditorStandaloneUtilityWindowContentController {
|
||||
public:
|
||||
explicit EditorAddComponentUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize);
|
||||
~EditorAddComponentUtilityWindowContentController() override;
|
||||
|
||||
private:
|
||||
void OnShutdown() override;
|
||||
void OnResetInteractionState() override;
|
||||
EditorWindowFrameTransferRequests UpdateStandaloneContent(
|
||||
const EditorStandaloneUtilityWindowFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) override;
|
||||
|
||||
AddComponentPanel m_addComponentPanel = {};
|
||||
};
|
||||
|
||||
std::unique_ptr<EditorWindowContentController>
|
||||
CreateEditorAddComponentUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize);
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -1,78 +1,38 @@
|
||||
#include "Platform/Win32/EditorColorPickerUtilityWindowContentController.h"
|
||||
|
||||
#include <XCEditor/Docking/UIEditorDockHostTransfer.h>
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
|
||||
EditorColorPickerUtilityWindowContentController::
|
||||
EditorColorPickerUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize)
|
||||
: m_minimumOuterSize(minimumOuterSize) {}
|
||||
: EditorStandaloneUtilityWindowContentController(minimumOuterSize) {}
|
||||
|
||||
EditorColorPickerUtilityWindowContentController::
|
||||
~EditorColorPickerUtilityWindowContentController() = default;
|
||||
|
||||
const UIEditorWorkspaceController*
|
||||
EditorColorPickerUtilityWindowContentController::TryGetWorkspaceController() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceController*
|
||||
EditorColorPickerUtilityWindowContentController::TryGetMutableWorkspaceController() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::ReplaceWorkspaceController(
|
||||
UIEditorWorkspaceController) {}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::Initialize(
|
||||
const EditorWindowContentInitializationContext&) {}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::Shutdown() {
|
||||
m_colorPickerPanel.ResetInteractionState();
|
||||
m_shellInteractionState = {};
|
||||
m_shellFrame = {};
|
||||
m_windowFocused = false;
|
||||
}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::ResetInteractionState() {
|
||||
void EditorColorPickerUtilityWindowContentController::OnShutdown() {
|
||||
m_colorPickerPanel.ResetInteractionState();
|
||||
}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::SetViewportSurfacePresentationEnabled(bool) {}
|
||||
void EditorColorPickerUtilityWindowContentController::OnResetInteractionState() {
|
||||
m_colorPickerPanel.ResetInteractionState();
|
||||
}
|
||||
|
||||
EditorWindowFrameTransferRequests
|
||||
EditorColorPickerUtilityWindowContentController::UpdateAndAppend(
|
||||
const EditorWindowContentFrameContext& context,
|
||||
EditorColorPickerUtilityWindowContentController::UpdateStandaloneContent(
|
||||
const EditorStandaloneUtilityWindowFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) {
|
||||
bool focusGained = false;
|
||||
bool focusLost = false;
|
||||
for (const UIInputEvent& event : context.inputEvents) {
|
||||
if (event.type == UIInputEventType::FocusGained) {
|
||||
m_windowFocused = true;
|
||||
focusGained = true;
|
||||
} else if (event.type == UIInputEventType::FocusLost) {
|
||||
m_windowFocused = false;
|
||||
focusLost = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_shellInteractionState.focused = m_windowFocused;
|
||||
m_shellFrame.focused = m_windowFocused;
|
||||
m_colorPickerPanel.Update(
|
||||
context.editorContext,
|
||||
ColorPickerPanelHostContext{
|
||||
.mounted = true,
|
||||
.bounds = context.bounds,
|
||||
.allowInteraction = true,
|
||||
.focused = m_windowFocused,
|
||||
.focusGained = focusGained,
|
||||
.focusLost = focusLost,
|
||||
.focused = context.focused,
|
||||
.focusGained = context.focusGained,
|
||||
.focusLost = context.focusLost,
|
||||
},
|
||||
context.inputEvents);
|
||||
|
||||
@@ -82,79 +42,6 @@ EditorColorPickerUtilityWindowContentController::UpdateAndAppend(
|
||||
return {};
|
||||
}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::RenderRequestedViewports(
|
||||
const ::XCEngine::Rendering::RenderContext&) {}
|
||||
|
||||
const UIEditorShellInteractionFrame&
|
||||
EditorColorPickerUtilityWindowContentController::GetShellFrame() const {
|
||||
return m_shellFrame;
|
||||
}
|
||||
|
||||
const UIEditorShellInteractionState&
|
||||
EditorColorPickerUtilityWindowContentController::GetShellInteractionState() const {
|
||||
return m_shellInteractionState;
|
||||
}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::SetExternalDockHostDropPreview(
|
||||
const Widgets::UIEditorDockHostDropPreviewState&) {}
|
||||
|
||||
void EditorColorPickerUtilityWindowContentController::ClearExternalDockHostDropPreview() {}
|
||||
|
||||
bool EditorColorPickerUtilityWindowContentController::TryResolveDockTabDragHotspot(
|
||||
std::string_view,
|
||||
std::string_view,
|
||||
const ::XCEngine::UI::UIPoint&,
|
||||
::XCEngine::UI::UIPoint&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorDockHostTabDropTarget
|
||||
EditorColorPickerUtilityWindowContentController::ResolveDockTabDropTarget(
|
||||
const ::XCEngine::UI::UIPoint&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool EditorColorPickerUtilityWindowContentController::HasHostedContentCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorColorPickerUtilityWindowContentController::HasShellInteractiveCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorColorPickerUtilityWindowContentController::HasInteractiveCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorWindowContentCursorKind
|
||||
EditorColorPickerUtilityWindowContentController::GetHostedContentCursorKind() const {
|
||||
return EditorWindowContentCursorKind::Arrow;
|
||||
}
|
||||
|
||||
EditorWindowContentCursorKind
|
||||
EditorColorPickerUtilityWindowContentController::GetDockCursorKind() const {
|
||||
return EditorWindowContentCursorKind::Arrow;
|
||||
}
|
||||
|
||||
::XCEngine::UI::UISize
|
||||
EditorColorPickerUtilityWindowContentController::ResolveMinimumOuterSize() const {
|
||||
return m_minimumOuterSize;
|
||||
}
|
||||
|
||||
bool EditorColorPickerUtilityWindowContentController::ShouldUseDetachedTitleBarTabStrip() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string EditorColorPickerUtilityWindowContentController::ResolveTabStripTitleText(
|
||||
std::string_view fallbackTitle) const {
|
||||
return std::string(fallbackTitle);
|
||||
}
|
||||
|
||||
std::string EditorColorPickerUtilityWindowContentController::ResolveDetachedWindowTitleText(
|
||||
std::string_view fallbackWindowTitle) const {
|
||||
return std::string(fallbackWindowTitle);
|
||||
}
|
||||
|
||||
std::unique_ptr<EditorWindowContentController>
|
||||
CreateEditorColorPickerUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize) {
|
||||
|
||||
@@ -1,71 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Features/ColorPicker/ColorPickerPanel.h"
|
||||
#include "Platform/Win32/EditorWindowContentController.h"
|
||||
|
||||
#include <XCEditor/Shell/UIEditorShellInteraction.h>
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
#include "Platform/Win32/EditorStandaloneUtilityWindowContentController.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorColorPickerUtilityWindowContentController final
|
||||
: public EditorWindowContentController {
|
||||
: public EditorStandaloneUtilityWindowContentController {
|
||||
public:
|
||||
explicit EditorColorPickerUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize);
|
||||
~EditorColorPickerUtilityWindowContentController() override;
|
||||
|
||||
const UIEditorWorkspaceController* TryGetWorkspaceController() const override;
|
||||
UIEditorWorkspaceController* TryGetMutableWorkspaceController() override;
|
||||
void ReplaceWorkspaceController(UIEditorWorkspaceController workspaceController) override;
|
||||
|
||||
void Initialize(const EditorWindowContentInitializationContext& context) override;
|
||||
void Shutdown() override;
|
||||
void ResetInteractionState() override;
|
||||
void SetViewportSurfacePresentationEnabled(bool enabled) override;
|
||||
|
||||
EditorWindowFrameTransferRequests UpdateAndAppend(
|
||||
const EditorWindowContentFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) override;
|
||||
void RenderRequestedViewports(
|
||||
const ::XCEngine::Rendering::RenderContext& renderContext) override;
|
||||
|
||||
const UIEditorShellInteractionFrame& GetShellFrame() const override;
|
||||
const UIEditorShellInteractionState& GetShellInteractionState() const override;
|
||||
|
||||
void SetExternalDockHostDropPreview(
|
||||
const Widgets::UIEditorDockHostDropPreviewState& preview) override;
|
||||
void ClearExternalDockHostDropPreview() override;
|
||||
|
||||
bool TryResolveDockTabDragHotspot(
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const ::XCEngine::UI::UIPoint& point,
|
||||
::XCEngine::UI::UIPoint& outHotspot) const override;
|
||||
UIEditorDockHostTabDropTarget ResolveDockTabDropTarget(
|
||||
const ::XCEngine::UI::UIPoint& point) const override;
|
||||
|
||||
bool HasHostedContentCapture() const override;
|
||||
bool HasShellInteractiveCapture() const override;
|
||||
bool HasInteractiveCapture() const override;
|
||||
EditorWindowContentCursorKind GetHostedContentCursorKind() const override;
|
||||
EditorWindowContentCursorKind GetDockCursorKind() const override;
|
||||
|
||||
::XCEngine::UI::UISize ResolveMinimumOuterSize() const override;
|
||||
bool ShouldUseDetachedTitleBarTabStrip() const override;
|
||||
std::string ResolveTabStripTitleText(std::string_view fallbackTitle) const override;
|
||||
std::string ResolveDetachedWindowTitleText(
|
||||
std::string_view fallbackWindowTitle) const override;
|
||||
|
||||
private:
|
||||
::XCEngine::UI::UISize m_minimumOuterSize = {};
|
||||
void OnShutdown() override;
|
||||
void OnResetInteractionState() override;
|
||||
EditorWindowFrameTransferRequests UpdateStandaloneContent(
|
||||
const EditorStandaloneUtilityWindowFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) override;
|
||||
|
||||
ColorPickerPanel m_colorPickerPanel = {};
|
||||
UIEditorShellInteractionState m_shellInteractionState = {};
|
||||
UIEditorShellInteractionFrame m_shellFrame = {};
|
||||
bool m_windowFocused = true;
|
||||
};
|
||||
|
||||
std::unique_ptr<EditorWindowContentController>
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "Platform/Win32/EditorStandaloneUtilityWindowContentController.h"
|
||||
|
||||
#include <XCEditor/Docking/UIEditorDockHostTransfer.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
|
||||
EditorStandaloneUtilityWindowContentController::
|
||||
EditorStandaloneUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize)
|
||||
: m_minimumOuterSize(minimumOuterSize) {}
|
||||
|
||||
EditorStandaloneUtilityWindowContentController::
|
||||
~EditorStandaloneUtilityWindowContentController() = default;
|
||||
|
||||
const UIEditorWorkspaceController*
|
||||
EditorStandaloneUtilityWindowContentController::TryGetWorkspaceController() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceController*
|
||||
EditorStandaloneUtilityWindowContentController::TryGetMutableWorkspaceController() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::ReplaceWorkspaceController(
|
||||
UIEditorWorkspaceController) {}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::Initialize(
|
||||
const EditorWindowContentInitializationContext&) {}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::Shutdown() {
|
||||
OnShutdown();
|
||||
m_shellInteractionState = {};
|
||||
m_shellFrame = {};
|
||||
m_windowFocused = false;
|
||||
}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::ResetInteractionState() {
|
||||
OnResetInteractionState();
|
||||
}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::SetViewportSurfacePresentationEnabled(bool) {}
|
||||
|
||||
EditorWindowFrameTransferRequests
|
||||
EditorStandaloneUtilityWindowContentController::UpdateAndAppend(
|
||||
const EditorWindowContentFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) {
|
||||
bool focusGained = false;
|
||||
bool focusLost = false;
|
||||
for (const UIInputEvent& event : context.inputEvents) {
|
||||
if (event.type == UIInputEventType::FocusGained) {
|
||||
m_windowFocused = true;
|
||||
focusGained = true;
|
||||
} else if (event.type == UIInputEventType::FocusLost) {
|
||||
m_windowFocused = false;
|
||||
focusLost = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_shellInteractionState.focused = m_windowFocused;
|
||||
m_shellFrame.focused = m_windowFocused;
|
||||
return UpdateStandaloneContent(
|
||||
EditorStandaloneUtilityWindowFrameContext{
|
||||
.editorContext = context.editorContext,
|
||||
.bounds = context.bounds,
|
||||
.inputEvents = context.inputEvents,
|
||||
.focused = m_windowFocused,
|
||||
.focusGained = focusGained,
|
||||
.focusLost = focusLost,
|
||||
},
|
||||
drawData);
|
||||
}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::RenderRequestedViewports(
|
||||
const ::XCEngine::Rendering::RenderContext&) {}
|
||||
|
||||
const UIEditorShellInteractionFrame&
|
||||
EditorStandaloneUtilityWindowContentController::GetShellFrame() const {
|
||||
return m_shellFrame;
|
||||
}
|
||||
|
||||
const UIEditorShellInteractionState&
|
||||
EditorStandaloneUtilityWindowContentController::GetShellInteractionState() const {
|
||||
return m_shellInteractionState;
|
||||
}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::SetExternalDockHostDropPreview(
|
||||
const Widgets::UIEditorDockHostDropPreviewState&) {}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::ClearExternalDockHostDropPreview() {}
|
||||
|
||||
bool EditorStandaloneUtilityWindowContentController::TryResolveDockTabDragHotspot(
|
||||
std::string_view,
|
||||
std::string_view,
|
||||
const ::XCEngine::UI::UIPoint&,
|
||||
::XCEngine::UI::UIPoint&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorDockHostTabDropTarget
|
||||
EditorStandaloneUtilityWindowContentController::ResolveDockTabDropTarget(
|
||||
const ::XCEngine::UI::UIPoint&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool EditorStandaloneUtilityWindowContentController::HasHostedContentCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorStandaloneUtilityWindowContentController::HasShellInteractiveCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorStandaloneUtilityWindowContentController::HasInteractiveCapture() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorWindowContentCursorKind
|
||||
EditorStandaloneUtilityWindowContentController::GetHostedContentCursorKind() const {
|
||||
return EditorWindowContentCursorKind::Arrow;
|
||||
}
|
||||
|
||||
EditorWindowContentCursorKind
|
||||
EditorStandaloneUtilityWindowContentController::GetDockCursorKind() const {
|
||||
return EditorWindowContentCursorKind::Arrow;
|
||||
}
|
||||
|
||||
::XCEngine::UI::UISize
|
||||
EditorStandaloneUtilityWindowContentController::ResolveMinimumOuterSize() const {
|
||||
return m_minimumOuterSize;
|
||||
}
|
||||
|
||||
bool EditorStandaloneUtilityWindowContentController::ShouldUseDetachedTitleBarTabStrip() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string EditorStandaloneUtilityWindowContentController::ResolveTabStripTitleText(
|
||||
std::string_view fallbackTitle) const {
|
||||
return std::string(fallbackTitle);
|
||||
}
|
||||
|
||||
std::string EditorStandaloneUtilityWindowContentController::ResolveDetachedWindowTitleText(
|
||||
std::string_view fallbackWindowTitle) const {
|
||||
return std::string(fallbackWindowTitle);
|
||||
}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::OnShutdown() {}
|
||||
|
||||
void EditorStandaloneUtilityWindowContentController::OnResetInteractionState() {}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include "Platform/Win32/EditorWindowContentController.h"
|
||||
|
||||
#include <XCEditor/Shell/UIEditorShellInteraction.h>
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
struct EditorStandaloneUtilityWindowFrameContext {
|
||||
EditorContext& editorContext;
|
||||
const ::XCEngine::UI::UIRect& bounds;
|
||||
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents;
|
||||
bool focused = false;
|
||||
bool focusGained = false;
|
||||
bool focusLost = false;
|
||||
};
|
||||
|
||||
class EditorStandaloneUtilityWindowContentController
|
||||
: public EditorWindowContentController {
|
||||
public:
|
||||
explicit EditorStandaloneUtilityWindowContentController(
|
||||
const ::XCEngine::UI::UISize& minimumOuterSize);
|
||||
~EditorStandaloneUtilityWindowContentController() override;
|
||||
|
||||
const UIEditorWorkspaceController* TryGetWorkspaceController() const override;
|
||||
UIEditorWorkspaceController* TryGetMutableWorkspaceController() override;
|
||||
void ReplaceWorkspaceController(UIEditorWorkspaceController workspaceController) override;
|
||||
|
||||
void Initialize(const EditorWindowContentInitializationContext& context) override;
|
||||
void Shutdown() override;
|
||||
void ResetInteractionState() override;
|
||||
void SetViewportSurfacePresentationEnabled(bool enabled) override;
|
||||
|
||||
EditorWindowFrameTransferRequests UpdateAndAppend(
|
||||
const EditorWindowContentFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) override;
|
||||
void RenderRequestedViewports(
|
||||
const ::XCEngine::Rendering::RenderContext& renderContext) override;
|
||||
|
||||
const UIEditorShellInteractionFrame& GetShellFrame() const override;
|
||||
const UIEditorShellInteractionState& GetShellInteractionState() const override;
|
||||
|
||||
void SetExternalDockHostDropPreview(
|
||||
const Widgets::UIEditorDockHostDropPreviewState& preview) override;
|
||||
void ClearExternalDockHostDropPreview() override;
|
||||
|
||||
bool TryResolveDockTabDragHotspot(
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const ::XCEngine::UI::UIPoint& point,
|
||||
::XCEngine::UI::UIPoint& outHotspot) const override;
|
||||
UIEditorDockHostTabDropTarget ResolveDockTabDropTarget(
|
||||
const ::XCEngine::UI::UIPoint& point) const override;
|
||||
|
||||
bool HasHostedContentCapture() const override;
|
||||
bool HasShellInteractiveCapture() const override;
|
||||
bool HasInteractiveCapture() const override;
|
||||
EditorWindowContentCursorKind GetHostedContentCursorKind() const override;
|
||||
EditorWindowContentCursorKind GetDockCursorKind() const override;
|
||||
|
||||
::XCEngine::UI::UISize ResolveMinimumOuterSize() const override;
|
||||
bool ShouldUseDetachedTitleBarTabStrip() const override;
|
||||
std::string ResolveTabStripTitleText(std::string_view fallbackTitle) const override;
|
||||
std::string ResolveDetachedWindowTitleText(
|
||||
std::string_view fallbackWindowTitle) const override;
|
||||
|
||||
protected:
|
||||
virtual void OnShutdown();
|
||||
virtual void OnResetInteractionState();
|
||||
virtual EditorWindowFrameTransferRequests UpdateStandaloneContent(
|
||||
const EditorStandaloneUtilityWindowFrameContext& context,
|
||||
::XCEngine::UI::UIDrawData& drawData) = 0;
|
||||
|
||||
private:
|
||||
::XCEngine::UI::UISize m_minimumOuterSize = {};
|
||||
UIEditorShellInteractionState m_shellInteractionState = {};
|
||||
UIEditorShellInteractionFrame m_shellFrame = {};
|
||||
bool m_windowFocused = true;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -7,6 +7,7 @@ namespace XCEngine::UI::Editor::App {
|
||||
enum class EditorUtilityWindowKind : std::uint8_t {
|
||||
None = 0,
|
||||
ColorPicker,
|
||||
AddComponent,
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Platform/Win32/EditorUtilityWindowRegistry.h"
|
||||
|
||||
#include "Platform/Win32/EditorAddComponentUtilityWindowContentController.h"
|
||||
#include "Platform/Win32/EditorColorPickerUtilityWindowContentController.h"
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
@@ -16,6 +17,14 @@ constexpr EditorUtilityWindowDescriptor kColorPickerUtilityWindowDescriptor = {
|
||||
.minimumOuterSize = UISize(320.0f, 460.0f),
|
||||
};
|
||||
|
||||
constexpr EditorUtilityWindowDescriptor kAddComponentUtilityWindowDescriptor = {
|
||||
.kind = EditorUtilityWindowKind::AddComponent,
|
||||
.windowId = "utility.add-component",
|
||||
.title = L"Add Component",
|
||||
.preferredOuterSize = UISize(352.0f, 500.0f),
|
||||
.minimumOuterSize = UISize(320.0f, 460.0f),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const EditorUtilityWindowDescriptor* ResolveEditorUtilityWindowDescriptor(
|
||||
@@ -23,6 +32,8 @@ const EditorUtilityWindowDescriptor* ResolveEditorUtilityWindowDescriptor(
|
||||
switch (kind) {
|
||||
case EditorUtilityWindowKind::ColorPicker:
|
||||
return &kColorPickerUtilityWindowDescriptor;
|
||||
case EditorUtilityWindowKind::AddComponent:
|
||||
return &kAddComponentUtilityWindowDescriptor;
|
||||
case EditorUtilityWindowKind::None:
|
||||
default:
|
||||
return nullptr;
|
||||
@@ -41,6 +52,9 @@ std::unique_ptr<EditorWindowContentController> CreateEditorUtilityWindowContentC
|
||||
case EditorUtilityWindowKind::ColorPicker:
|
||||
return CreateEditorColorPickerUtilityWindowContentController(
|
||||
descriptor->minimumOuterSize);
|
||||
case EditorUtilityWindowKind::AddComponent:
|
||||
return CreateEditorAddComponentUtilityWindowContentController(
|
||||
descriptor->minimumOuterSize);
|
||||
case EditorUtilityWindowKind::None:
|
||||
default:
|
||||
return nullptr;
|
||||
|
||||
@@ -80,10 +80,11 @@ EditorWindowFrameTransferRequests EditorWindowFrameOrchestrator::UpdateAndAppend
|
||||
EditorWindowFrameTransferRequests transferRequests =
|
||||
BuildShellTransferRequests(globalTabDragActive, dockHostInteractionState, shellFrame);
|
||||
POINT screenPoint = {};
|
||||
if (ConsumeEditorColorPickerOpenUtilityWindowRequest(
|
||||
editorContext.GetColorPickerToolState())) {
|
||||
if (const std::optional<EditorUtilityWindowKind> requestedKind =
|
||||
editorContext.ConsumeOpenUtilityWindowRequest();
|
||||
requestedKind.has_value()) {
|
||||
transferRequests.openUtilityWindow = EditorWindowOpenUtilityWindowRequest{
|
||||
.kind = EditorUtilityWindowKind::ColorPicker,
|
||||
.kind = *requestedKind,
|
||||
.useCursorPlacement = GetCursorPos(&screenPoint) != FALSE,
|
||||
};
|
||||
if (transferRequests.openUtilityWindow->useCursorPlacement) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Scene/EditorSceneRuntime.h"
|
||||
|
||||
#include <XCEngine/Components/ComponentFactoryRegistry.h>
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/Component.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
@@ -472,6 +473,36 @@ bool EditorSceneRuntime::MoveGameObjectToRoot(std::string_view itemId) {
|
||||
return moved;
|
||||
}
|
||||
|
||||
bool EditorSceneRuntime::AddComponentToSelectedGameObject(
|
||||
std::string_view componentTypeName) {
|
||||
if (componentTypeName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::optional<GameObject::ID> selectedId = GetSelectedGameObjectId();
|
||||
Scene* scene = GetActiveScene();
|
||||
if (!selectedId.has_value() || scene == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GameObject* gameObject = scene->FindByID(selectedId.value());
|
||||
if (gameObject == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Component* addedComponent =
|
||||
::XCEngine::Components::ComponentFactoryRegistry::Get().CreateComponent(
|
||||
gameObject,
|
||||
std::string(componentTypeName));
|
||||
if (addedComponent == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IncrementInspectorRevision();
|
||||
RefreshScene();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorSceneRuntime::CanRemoveSelectedComponent(
|
||||
std::string_view componentId) const {
|
||||
const EditorSceneComponentDescriptor descriptor =
|
||||
|
||||
@@ -94,6 +94,7 @@ public:
|
||||
std::string_view itemId,
|
||||
std::string_view parentItemId);
|
||||
bool MoveGameObjectToRoot(std::string_view itemId);
|
||||
bool AddComponentToSelectedGameObject(std::string_view componentTypeName);
|
||||
bool CanRemoveSelectedComponent(std::string_view componentId) const;
|
||||
bool RemoveSelectedComponent(std::string_view componentId);
|
||||
bool SetSelectedTransformLocalPosition(
|
||||
|
||||
@@ -29,20 +29,12 @@ void OpenEditorColorPickerToolForInspectorField(
|
||||
bool showAlpha) {
|
||||
state.active = true;
|
||||
state.showAlpha = showAlpha;
|
||||
state.requestOpenUtilityWindow = true;
|
||||
state.color = color;
|
||||
state.inspectorTarget.subjectKey = std::string(subjectKey);
|
||||
state.inspectorTarget.fieldId = std::string(fieldId);
|
||||
++state.revision;
|
||||
}
|
||||
|
||||
bool ConsumeEditorColorPickerOpenUtilityWindowRequest(
|
||||
EditorColorPickerToolState& state) {
|
||||
const bool requested = state.requestOpenUtilityWindow;
|
||||
state.requestOpenUtilityWindow = false;
|
||||
return requested;
|
||||
}
|
||||
|
||||
bool UpdateEditorColorPickerToolColor(
|
||||
EditorColorPickerToolState& state,
|
||||
const ::XCEngine::UI::UIColor& color) {
|
||||
|
||||
@@ -16,7 +16,6 @@ struct EditorColorPickerInspectorTarget {
|
||||
struct EditorColorPickerToolState {
|
||||
bool active = false;
|
||||
bool showAlpha = true;
|
||||
bool requestOpenUtilityWindow = false;
|
||||
::XCEngine::UI::UIColor color = {};
|
||||
EditorColorPickerInspectorTarget inspectorTarget = {};
|
||||
std::uint64_t revision = 0u;
|
||||
@@ -29,8 +28,6 @@ void OpenEditorColorPickerToolForInspectorField(
|
||||
std::string_view fieldId,
|
||||
const ::XCEngine::UI::UIColor& color,
|
||||
bool showAlpha);
|
||||
bool ConsumeEditorColorPickerOpenUtilityWindowRequest(
|
||||
EditorColorPickerToolState& state);
|
||||
bool UpdateEditorColorPickerToolColor(
|
||||
EditorColorPickerToolState& state,
|
||||
const ::XCEngine::UI::UIColor& color);
|
||||
|
||||
26
new_editor/app/State/EditorUtilityWindowRequestState.cpp
Normal file
26
new_editor/app/State/EditorUtilityWindowRequestState.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "State/EditorUtilityWindowRequestState.h"
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
void ResetEditorUtilityWindowRequestState(EditorUtilityWindowRequestState& state) {
|
||||
state = {};
|
||||
}
|
||||
|
||||
void RequestEditorUtilityWindow(
|
||||
EditorUtilityWindowRequestState& state,
|
||||
EditorUtilityWindowKind kind) {
|
||||
if (kind == EditorUtilityWindowKind::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.pendingKind = kind;
|
||||
}
|
||||
|
||||
std::optional<EditorUtilityWindowKind> ConsumeEditorUtilityWindowRequest(
|
||||
EditorUtilityWindowRequestState& state) {
|
||||
const std::optional<EditorUtilityWindowKind> requestedKind = state.pendingKind;
|
||||
state.pendingKind.reset();
|
||||
return requestedKind;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
20
new_editor/app/State/EditorUtilityWindowRequestState.h
Normal file
20
new_editor/app/State/EditorUtilityWindowRequestState.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Platform/Win32/EditorUtilityWindowKind.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
struct EditorUtilityWindowRequestState {
|
||||
std::optional<EditorUtilityWindowKind> pendingKind = {};
|
||||
};
|
||||
|
||||
void ResetEditorUtilityWindowRequestState(EditorUtilityWindowRequestState& state);
|
||||
void RequestEditorUtilityWindow(
|
||||
EditorUtilityWindowRequestState& state,
|
||||
EditorUtilityWindowKind kind);
|
||||
std::optional<EditorUtilityWindowKind> ConsumeEditorUtilityWindowRequest(
|
||||
EditorUtilityWindowRequestState& state);
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
Reference in New Issue
Block a user