195 lines
6.4 KiB
TypeScript
195 lines
6.4 KiB
TypeScript
import { Core, Entity, HierarchySystem, HierarchyComponent } from '@esengine/ecs-framework';
|
|
import { EntityStoreService, MessageHub } from '@esengine/editor-core';
|
|
import { BaseCommand } from '../BaseCommand';
|
|
|
|
/**
|
|
* 拖放位置类型
|
|
*/
|
|
export enum DropPosition {
|
|
/** 在目标之前 */
|
|
BEFORE = 'before',
|
|
/** 在目标内部(作为子级) */
|
|
INSIDE = 'inside',
|
|
/** 在目标之后 */
|
|
AFTER = 'after'
|
|
}
|
|
|
|
/**
|
|
* 重新设置实体父级命令
|
|
*
|
|
* 支持拖拽重排功能,可以将实体移动到:
|
|
* - 另一个实体之前 (BEFORE)
|
|
* - 另一个实体内部作为子级 (INSIDE)
|
|
* - 另一个实体之后 (AFTER)
|
|
*/
|
|
export class ReparentEntityCommand extends BaseCommand {
|
|
private oldParentId: number | null;
|
|
private oldSiblingIndex: number;
|
|
|
|
constructor(
|
|
private entityStore: EntityStoreService,
|
|
private messageHub: MessageHub,
|
|
private entity: Entity,
|
|
private targetEntity: Entity,
|
|
private dropPosition: DropPosition
|
|
) {
|
|
super();
|
|
|
|
// 保存原始状态用于撤销
|
|
const hierarchy = entity.getComponent(HierarchyComponent);
|
|
this.oldParentId = hierarchy?.parentId ?? null;
|
|
|
|
// 获取在兄弟列表中的原始索引
|
|
this.oldSiblingIndex = this.getSiblingIndex(entity);
|
|
}
|
|
|
|
execute(): void {
|
|
const scene = Core.scene;
|
|
if (!scene) {
|
|
console.warn('[ReparentEntityCommand] No scene available');
|
|
return;
|
|
}
|
|
|
|
const hierarchySystem = scene.getSystem(HierarchySystem);
|
|
if (!hierarchySystem) {
|
|
console.warn('[ReparentEntityCommand] No HierarchySystem found');
|
|
return;
|
|
}
|
|
|
|
// 确保目标实体有 HierarchyComponent
|
|
if (!this.targetEntity.getComponent(HierarchyComponent)) {
|
|
this.targetEntity.addComponent(new HierarchyComponent());
|
|
}
|
|
|
|
console.log(`[ReparentEntityCommand] Moving ${this.entity.name} to ${this.targetEntity.name} (${this.dropPosition})`);
|
|
|
|
switch (this.dropPosition) {
|
|
case DropPosition.INSIDE:
|
|
// 移动到目标实体内部作为最后一个子级
|
|
hierarchySystem.setParent(this.entity, this.targetEntity);
|
|
break;
|
|
|
|
case DropPosition.BEFORE:
|
|
case DropPosition.AFTER:
|
|
// 移动到目标实体的同级
|
|
this.moveToSibling(hierarchySystem);
|
|
break;
|
|
}
|
|
|
|
this.entityStore.syncFromScene();
|
|
this.messageHub.publish('entity:reparented', {
|
|
entityId: this.entity.id,
|
|
targetId: this.targetEntity.id,
|
|
position: this.dropPosition
|
|
});
|
|
}
|
|
|
|
undo(): void {
|
|
const scene = Core.scene;
|
|
if (!scene) return;
|
|
|
|
const hierarchySystem = scene.getSystem(HierarchySystem);
|
|
if (!hierarchySystem) return;
|
|
|
|
// 恢复到原始父级
|
|
const oldParent = this.oldParentId !== null
|
|
? scene.findEntityById(this.oldParentId)
|
|
: null;
|
|
|
|
if (oldParent) {
|
|
// 恢复到原始父级的指定位置
|
|
hierarchySystem.insertChildAt(oldParent, this.entity, this.oldSiblingIndex);
|
|
} else {
|
|
// 恢复到根级
|
|
hierarchySystem.setParent(this.entity, null);
|
|
}
|
|
|
|
this.entityStore.syncFromScene();
|
|
this.messageHub.publish('entity:reparented', {
|
|
entityId: this.entity.id,
|
|
targetId: null,
|
|
position: 'undo'
|
|
});
|
|
}
|
|
|
|
getDescription(): string {
|
|
const positionText = this.dropPosition === DropPosition.INSIDE
|
|
? '移入'
|
|
: this.dropPosition === DropPosition.BEFORE ? '移动到前面' : '移动到后面';
|
|
return `${positionText}: ${this.entity.name} -> ${this.targetEntity.name}`;
|
|
}
|
|
|
|
/**
|
|
* 移动到目标的同级位置
|
|
*/
|
|
private moveToSibling(hierarchySystem: HierarchySystem): void {
|
|
const targetHierarchy = this.targetEntity.getComponent(HierarchyComponent);
|
|
const targetParentId = targetHierarchy?.parentId ?? null;
|
|
|
|
const scene = Core.scene;
|
|
if (!scene) return;
|
|
|
|
// 获取目标的父实体
|
|
const targetParent = targetParentId !== null
|
|
? scene.findEntityById(targetParentId)
|
|
: null;
|
|
|
|
// 获取目标在兄弟列表中的索引
|
|
let targetIndex = this.getSiblingIndex(this.targetEntity);
|
|
|
|
// 根据放置位置调整索引
|
|
if (this.dropPosition === DropPosition.AFTER) {
|
|
targetIndex++;
|
|
}
|
|
|
|
// 如果移动到同一父级下,需要考虑原位置对索引的影响
|
|
const entityHierarchy = this.entity.getComponent(HierarchyComponent);
|
|
const entityParentId = entityHierarchy?.parentId ?? null;
|
|
|
|
const bSameParent = entityParentId === targetParentId;
|
|
if (bSameParent) {
|
|
const currentIndex = this.getSiblingIndex(this.entity);
|
|
if (currentIndex < targetIndex) {
|
|
targetIndex--;
|
|
}
|
|
}
|
|
|
|
console.log(`[ReparentEntityCommand] moveToSibling: targetParent=${targetParent?.name ?? 'ROOT'}, targetIndex=${targetIndex}`);
|
|
|
|
if (targetParent) {
|
|
// 有父级,插入到父级的指定位置
|
|
hierarchySystem.insertChildAt(targetParent, this.entity, targetIndex);
|
|
} else {
|
|
// 目标在根级
|
|
// 先确保实体移动到根级
|
|
if (entityParentId !== null) {
|
|
hierarchySystem.setParent(this.entity, null);
|
|
}
|
|
// 然后调整根级顺序
|
|
this.entityStore.reorderEntity(this.entity.id, targetIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取实体在兄弟列表中的索引
|
|
*/
|
|
private getSiblingIndex(entity: Entity): number {
|
|
const scene = Core.scene;
|
|
if (!scene) return 0;
|
|
|
|
const hierarchy = entity.getComponent(HierarchyComponent);
|
|
const parentId = hierarchy?.parentId;
|
|
|
|
if (parentId === null || parentId === undefined) {
|
|
// 根级实体,从 EntityStoreService 获取
|
|
return this.entityStore.getRootEntityIds().indexOf(entity.id);
|
|
}
|
|
|
|
const parent = scene.findEntityById(parentId);
|
|
if (!parent) return 0;
|
|
|
|
const parentHierarchy = parent.getComponent(HierarchyComponent);
|
|
return parentHierarchy?.childIds.indexOf(entity.id) ?? 0;
|
|
}
|
|
}
|