Files
esengine/packages/rendering/fairygui/src/core/DragDropManager.ts

145 lines
3.9 KiB
TypeScript
Raw Normal View History

feat(fairygui): FairyGUI 完整集成 (#314) * feat(fairygui): FairyGUI ECS 集成核心架构 实现 FairyGUI 的 ECS 原生集成,完全替代旧 UI 系统: 核心类: - GObject: UI 对象基类,支持变换、可见性、关联、齿轮 - GComponent: 容器组件,管理子对象和控制器 - GRoot: 根容器,管理焦点、弹窗、输入分发 - GGroup: 组容器,支持水平/垂直布局 抽象层: - DisplayObject: 显示对象基类 - EventDispatcher: 事件分发 - Timer: 计时器 - Stage: 舞台,管理输入和缩放 布局系统: - Relations: 约束关联管理 - RelationItem: 24 种关联类型 基础设施: - Controller: 状态控制器 - Transition: 过渡动画 - ScrollPane: 滚动面板 - UIPackage: 包管理 - ByteBuffer: 二进制解析 * refactor(ui): 删除旧 UI 系统,使用 FairyGUI 替代 * feat(fairygui): 实现 UI 控件 - 添加显示类:Image、TextField、Graph - 添加基础控件:GImage、GTextField、GGraph - 添加交互控件:GButton、GProgressBar、GSlider - 更新 IRenderCollector 支持 Graph 渲染 - 扩展 Controller 添加 selectedPageId - 添加 STATE_CHANGED 事件类型 * feat(fairygui): 现代化架构重构 - 增强 EventDispatcher 支持类型安全、优先级和传播控制 - 添加 PropertyBinding 响应式属性绑定系统 - 添加 ServiceContainer 依赖注入容器 - 添加 UIConfig 全局配置系统 - 添加 UIObjectFactory 对象工厂 - 实现 RenderBridge 渲染桥接层 - 实现 Canvas2DBackend 作为默认渲染后端 - 扩展 IRenderCollector 支持更多图元类型 * feat(fairygui): 九宫格渲染和资源加载修复 - 修复 FGUIUpdateSystem 支持路径和 GUID 两种加载方式 - 修复 GTextInput 同时设置 _displayObject 和 _textField - 实现九宫格渲染展开为 9 个子图元 - 添加 sourceWidth/sourceHeight 用于九宫格计算 - 添加 DOMTextRenderer 文本渲染层(临时方案) * fix(fairygui): 修复 GGraph 颜色读取 * feat(fairygui): 虚拟节点 Inspector 和文本渲染支持 * fix(fairygui): 编辑器状态刷新和遗留引用修复 - 修复切换 FGUI 包后组件列表未刷新问题 - 修复切换组件后 viewport 未清理旧内容问题 - 修复虚拟节点在包加载后未刷新问题 - 重构为事件驱动架构,移除轮询机制 - 修复 @esengine/ui 遗留引用,统一使用 @esengine/fairygui * fix: 移除 tsconfig 中的 @esengine/ui 引用
2025-12-22 10:52:54 +08:00
import { GObject } from './GObject';
import { GRoot } from './GRoot';
import { GLoader } from '../widgets/GLoader';
import { Stage } from './Stage';
import { FGUIEvents } from '../events/Events';
import { EAlignType, EVertAlignType } from './FieldTypes';
/**
* DragDropManager
*
* Manages drag and drop operations with visual feedback.
*
*
*
* Features:
* - Visual drag agent with icon
* - Source data carrying
* - Drop target detection
* - Singleton pattern
*
* @example
* ```typescript
* // Start drag operation
* DragDropManager.inst.startDrag(sourceObj, 'ui://pkg/icon', myData);
*
* // Listen for drop on target
* targetObj.on(FGUIEvents.DROP, (data) => {
* console.log('Dropped:', data);
* });
*
* // Cancel drag
* DragDropManager.inst.cancel();
* ```
*/
export class DragDropManager {
private static _inst: DragDropManager | null = null;
private _agent: GLoader;
private _sourceData: any = null;
/**
* Get singleton instance
*
*/
public static get inst(): DragDropManager {
if (!DragDropManager._inst) {
DragDropManager._inst = new DragDropManager();
}
return DragDropManager._inst;
}
constructor() {
this._agent = new GLoader();
this._agent.draggable = true;
this._agent.touchable = false; // Important: prevent interference with drop detection
this._agent.setSize(100, 100);
this._agent.setPivot(0.5, 0.5, true);
this._agent.align = EAlignType.Center;
this._agent.verticalAlign = EVertAlignType.Middle;
this._agent.sortingOrder = 1000000;
this._agent.on(FGUIEvents.DRAG_END, this.onDragEnd, this);
}
/**
* Get drag agent object
*
*/
public get dragAgent(): GObject {
return this._agent;
}
/**
* Check if currently dragging
*
*/
public get dragging(): boolean {
return this._agent.parent !== null;
}
/**
* Start a drag operation
*
*
* @param source - Source object initiating drag |
* @param icon - Icon URL for drag agent | URL
* @param sourceData - Data to carry during drag |
* @param touchId - Touch point ID for multi-touch | ID
*/
public startDrag(source: GObject, icon: string, sourceData?: any, touchId?: number): void {
if (this._agent.parent) {
return;
}
this._sourceData = sourceData;
this._agent.url = icon;
GRoot.inst.addChild(this._agent);
const stage = Stage.inst;
const pt = GRoot.inst.globalToLocal(stage.mouseX, stage.mouseY);
this._agent.setXY(pt.x, pt.y);
this._agent.startDrag(touchId);
}
/**
* Cancel current drag operation
*
*/
public cancel(): void {
if (this._agent.parent) {
this._agent.stopDrag();
GRoot.inst.removeChild(this._agent);
this._sourceData = null;
}
}
private onDragEnd(): void {
if (!this._agent.parent) {
// Already cancelled
return;
}
GRoot.inst.removeChild(this._agent);
const sourceData = this._sourceData;
this._sourceData = null;
// Find drop target
const stage = Stage.inst;
const target = GRoot.inst.hitTest(stage.mouseX, stage.mouseY);
if (target) {
// Walk up the display list to find a drop handler
let obj: GObject | null = target;
while (obj) {
if (obj.hasListener(FGUIEvents.DROP)) {
obj.emit(FGUIEvents.DROP, sourceData);
return;
}
obj = obj.parent;
}
}
}
}