feat(new_editor): add standalone add-component utility window

This commit is contained in:
2026-04-22 22:07:02 +08:00
parent 865a35e4d0
commit 3048c7cc90
37 changed files with 1237 additions and 290 deletions

View File

@@ -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(