Files
XCEngine/new_editor/src/Fields/UIEditorAssetFieldInteraction.cpp

261 lines
9.0 KiB
C++
Raw Normal View History

2026-04-10 00:41:28 +08:00
#include <XCEditor/Fields/UIEditorAssetFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorAssetFieldLayout;
using Widgets::HasUIEditorAssetFieldValue;
using Widgets::HitTestUIEditorAssetField;
using Widgets::IsUIEditorAssetFieldPointInside;
using Widgets::UIEditorAssetFieldHitTarget;
using Widgets::UIEditorAssetFieldHitTargetKind;
using Widgets::UIEditorAssetFieldLayout;
using Widgets::UIEditorAssetFieldMetrics;
using Widgets::UIEditorAssetFieldSpec;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
return true;
default:
return false;
}
}
void SyncHoverTarget(
UIEditorAssetFieldInteractionState& state,
const UIEditorAssetFieldLayout& layout) {
if (!state.hasPointerPosition) {
state.fieldState.hoveredTarget = UIEditorAssetFieldHitTargetKind::None;
return;
}
state.fieldState.hoveredTarget = HitTestUIEditorAssetField(layout, state.pointerPosition).kind;
}
bool CanRequestPicker(const UIEditorAssetFieldSpec& spec) {
return !spec.readOnly && spec.showPickerButton;
}
bool CanClearValue(const UIEditorAssetFieldSpec& spec) {
return !spec.readOnly && spec.allowClear && HasUIEditorAssetFieldValue(spec);
}
void ClearValue(
UIEditorAssetFieldSpec& spec,
UIEditorAssetFieldInteractionResult& result) {
result.assetIdBefore = spec.assetId;
result.displayNameBefore = spec.displayName;
spec.assetId.clear();
spec.displayName.clear();
spec.statusText.clear();
result.assetIdAfter = spec.assetId;
result.displayNameAfter = spec.displayName;
result.clearRequested = true;
result.valueChanged = true;
result.consumed = true;
}
void RequestPicker(UIEditorAssetFieldInteractionResult& result) {
result.pickerRequested = true;
result.consumed = true;
}
void RequestActivate(UIEditorAssetFieldInteractionResult& result) {
result.activateRequested = true;
result.consumed = true;
}
} // namespace
UIEditorAssetFieldInteractionFrame UpdateUIEditorAssetFieldInteraction(
UIEditorAssetFieldInteractionState& state,
UIEditorAssetFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIInputEvent>& inputEvents,
const UIEditorAssetFieldMetrics& metrics) {
UIEditorAssetFieldLayout layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
SyncHoverTarget(state, layout);
UIEditorAssetFieldInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorAssetFieldInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
eventResult.focusChanged = !state.fieldState.focused;
state.fieldState.focused = true;
break;
case UIInputEventType::FocusLost:
eventResult.focusChanged = state.fieldState.focused;
state.fieldState.focused = false;
state.fieldState.activeTarget = UIEditorAssetFieldHitTargetKind::None;
state.hasPointerPosition = false;
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
break;
case UIInputEventType::PointerButtonDown: {
const UIEditorAssetFieldHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorAssetField(layout, state.pointerPosition)
: UIEditorAssetFieldHitTarget {};
eventResult.hitTarget = hitTarget;
if (event.pointerButton != UIPointerButton::Left) {
break;
}
const bool insideField =
state.hasPointerPosition &&
IsUIEditorAssetFieldPointInside(layout.bounds, state.pointerPosition);
if (insideField) {
eventResult.focusChanged = !state.fieldState.focused;
state.fieldState.focused = true;
state.fieldState.activeTarget =
hitTarget.kind == UIEditorAssetFieldHitTargetKind::None
? UIEditorAssetFieldHitTargetKind::Row
: hitTarget.kind;
eventResult.consumed = true;
} else {
if (state.fieldState.focused) {
eventResult.focusChanged = true;
state.fieldState.focused = false;
}
state.fieldState.activeTarget = UIEditorAssetFieldHitTargetKind::None;
}
break;
}
case UIInputEventType::PointerButtonUp: {
const UIEditorAssetFieldHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorAssetField(layout, state.pointerPosition)
: UIEditorAssetFieldHitTarget {};
eventResult.hitTarget = hitTarget;
if (event.pointerButton == UIPointerButton::Left) {
const UIEditorAssetFieldHitTargetKind activeTarget = state.fieldState.activeTarget;
state.fieldState.activeTarget = UIEditorAssetFieldHitTargetKind::None;
if (activeTarget == hitTarget.kind) {
switch (activeTarget) {
case UIEditorAssetFieldHitTargetKind::PickerButton:
if (CanRequestPicker(spec)) {
RequestPicker(eventResult);
}
break;
case UIEditorAssetFieldHitTargetKind::ClearButton:
if (CanClearValue(spec)) {
ClearValue(spec, eventResult);
}
break;
case UIEditorAssetFieldHitTargetKind::ValueBox:
RequestActivate(eventResult);
break;
case UIEditorAssetFieldHitTargetKind::Row:
eventResult.consumed = true;
break;
case UIEditorAssetFieldHitTargetKind::None:
default:
break;
}
}
}
break;
}
case UIInputEventType::KeyDown:
if (!state.fieldState.focused) {
break;
}
switch (static_cast<KeyCode>(event.keyCode)) {
case KeyCode::Enter:
case KeyCode::Space:
if (CanRequestPicker(spec)) {
RequestPicker(eventResult);
eventResult.hitTarget.kind = UIEditorAssetFieldHitTargetKind::PickerButton;
} else {
RequestActivate(eventResult);
eventResult.hitTarget.kind = UIEditorAssetFieldHitTargetKind::ValueBox;
}
break;
case KeyCode::Delete:
case KeyCode::Backspace:
if (CanClearValue(spec)) {
ClearValue(spec, eventResult);
eventResult.hitTarget.kind = UIEditorAssetFieldHitTargetKind::ClearButton;
}
break;
default:
break;
}
break;
default:
break;
}
layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
SyncHoverTarget(state, layout);
if (eventResult.hitTarget.kind == UIEditorAssetFieldHitTargetKind::None &&
state.hasPointerPosition) {
eventResult.hitTarget = HitTestUIEditorAssetField(layout, state.pointerPosition);
}
if (eventResult.consumed ||
eventResult.focusChanged ||
eventResult.valueChanged ||
eventResult.activateRequested ||
eventResult.pickerRequested ||
eventResult.clearRequested ||
eventResult.hitTarget.kind != UIEditorAssetFieldHitTargetKind::None) {
interactionResult = std::move(eventResult);
}
}
layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
SyncHoverTarget(state, layout);
if (interactionResult.hitTarget.kind == UIEditorAssetFieldHitTargetKind::None &&
state.hasPointerPosition) {
interactionResult.hitTarget = HitTestUIEditorAssetField(layout, state.pointerPosition);
}
return {
std::move(layout),
std::move(interactionResult)
};
}
} // namespace XCEngine::UI::Editor