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 引用
This commit is contained in:
70
packages/fairygui/src/gears/GearAnimation.ts
Normal file
70
packages/fairygui/src/gears/GearAnimation.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { EObjectPropID } from '../core/FieldTypes';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* Animation value for GearAnimation
|
||||
* GearAnimation 的动画值
|
||||
*/
|
||||
interface IAnimationValue {
|
||||
playing: boolean;
|
||||
frame: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* GearAnimation
|
||||
*
|
||||
* Controls object animation state based on controller state.
|
||||
* 根据控制器状态控制对象动画状态
|
||||
*/
|
||||
export class GearAnimation extends GearBase {
|
||||
private _storage: Map<string, IAnimationValue> = new Map();
|
||||
private _default: IAnimationValue = { playing: true, frame: 0 };
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = {
|
||||
playing: (this.owner.getProp(EObjectPropID.Playing) as boolean) ?? true,
|
||||
frame: (this.owner.getProp(EObjectPropID.Frame) as number) ?? 0
|
||||
};
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.setProp(EObjectPropID.Playing, gv.playing);
|
||||
this.owner.setProp(EObjectPropID.Frame, gv.frame);
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv: IAnimationValue = {
|
||||
playing: (this.owner.getProp(EObjectPropID.Playing) as boolean) ?? true,
|
||||
frame: (this.owner.getProp(EObjectPropID.Frame) as number) ?? 0
|
||||
};
|
||||
|
||||
this._storage.set(this._controller.selectedPageId, gv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status
|
||||
* 添加状态
|
||||
*/
|
||||
public addStatus(pageId: string | null, playing: boolean, frame: number): void {
|
||||
if (pageId === null) {
|
||||
this._default.playing = playing;
|
||||
this._default.frame = frame;
|
||||
} else {
|
||||
this._storage.set(pageId, { playing, frame });
|
||||
}
|
||||
}
|
||||
}
|
||||
152
packages/fairygui/src/gears/GearBase.ts
Normal file
152
packages/fairygui/src/gears/GearBase.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import type { GObject } from '../core/GObject';
|
||||
import type { Controller } from '../core/Controller';
|
||||
import type { ByteBuffer } from '../utils/ByteBuffer';
|
||||
import { EEaseType } from '../core/FieldTypes';
|
||||
|
||||
/**
|
||||
* GearBase
|
||||
*
|
||||
* Base class for all gear types.
|
||||
* Gears connect object properties to controller states.
|
||||
*
|
||||
* 所有齿轮类型的基类,齿轮将对象属性连接到控制器状态
|
||||
*/
|
||||
export abstract class GearBase {
|
||||
/** Owner object | 所有者对象 */
|
||||
public readonly owner: GObject;
|
||||
|
||||
/** Controller | 控制器 */
|
||||
protected _controller: Controller | null = null;
|
||||
|
||||
/** Tween config | 缓动配置 */
|
||||
public tweenConfig: GearTweenConfig | null = null;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controller
|
||||
* 获取控制器
|
||||
*/
|
||||
public get controller(): Controller | null {
|
||||
return this._controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set controller
|
||||
* 设置控制器
|
||||
*/
|
||||
public set controller(value: Controller | null) {
|
||||
if (this._controller !== value) {
|
||||
this._controller = value;
|
||||
if (this._controller) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if connected to a controller
|
||||
* 检查是否连接到控制器
|
||||
*/
|
||||
public get connected(): boolean {
|
||||
return this._controller !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize gear
|
||||
* 初始化齿轮
|
||||
*/
|
||||
protected abstract init(): void;
|
||||
|
||||
/**
|
||||
* Apply gear values
|
||||
* 应用齿轮值
|
||||
*/
|
||||
public abstract apply(): void;
|
||||
|
||||
/**
|
||||
* Update current state
|
||||
* 更新当前状态
|
||||
*/
|
||||
public abstract updateState(): void;
|
||||
|
||||
/**
|
||||
* Setup gear from buffer
|
||||
* 从缓冲区设置齿轮
|
||||
*/
|
||||
public setup(buffer: ByteBuffer): void {
|
||||
const parent = this.owner.parent;
|
||||
if (!parent) return;
|
||||
|
||||
this._controller = parent.getControllerAt(buffer.getInt16());
|
||||
this.init();
|
||||
|
||||
const cnt = buffer.getInt16();
|
||||
|
||||
// Read pages - subclasses should override to parse their specific data
|
||||
this.readStatusFromBuffer(buffer, cnt);
|
||||
|
||||
// Read default status
|
||||
if (buffer.readBool()) {
|
||||
this.readDefaultStatusFromBuffer(buffer);
|
||||
}
|
||||
|
||||
// Read tween config
|
||||
if (buffer.readBool()) {
|
||||
this.tweenConfig = new GearTweenConfig();
|
||||
this.tweenConfig.easeType = buffer.readByte() as EEaseType;
|
||||
this.tweenConfig.duration = buffer.getFloat32();
|
||||
this.tweenConfig.delay = buffer.getFloat32();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read status data from buffer
|
||||
* 从缓冲区读取状态数据
|
||||
*/
|
||||
protected readStatusFromBuffer(_buffer: ByteBuffer, _cnt: number): void {
|
||||
// Override in subclasses to parse specific gear data
|
||||
// Default: skip the data (each page has a string ID)
|
||||
for (let i = 0; i < _cnt; i++) {
|
||||
_buffer.readS(); // page id
|
||||
// Subclass should read its specific data here
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read default status from buffer
|
||||
* 从缓冲区读取默认状态
|
||||
*/
|
||||
protected readDefaultStatusFromBuffer(_buffer: ByteBuffer): void {
|
||||
// Override in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose
|
||||
* 销毁
|
||||
*/
|
||||
public dispose(): void {
|
||||
this._controller = null;
|
||||
this.tweenConfig = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gear tween configuration
|
||||
* 齿轮缓动配置
|
||||
*/
|
||||
export class GearTweenConfig {
|
||||
/** Tween enabled | 是否启用缓动 */
|
||||
public tween: boolean = true;
|
||||
|
||||
/** Ease type | 缓动类型 */
|
||||
public easeType: EEaseType = EEaseType.QuadOut;
|
||||
|
||||
/** Duration in seconds | 持续时间(秒) */
|
||||
public duration: number = 0.3;
|
||||
|
||||
/** Delay in seconds | 延迟时间(秒) */
|
||||
public delay: number = 0;
|
||||
}
|
||||
74
packages/fairygui/src/gears/GearColor.ts
Normal file
74
packages/fairygui/src/gears/GearColor.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { EObjectPropID } from '../core/FieldTypes';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* Color value for GearColor
|
||||
* GearColor 的颜色值
|
||||
*/
|
||||
interface IColorValue {
|
||||
color: number | null;
|
||||
strokeColor: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* GearColor
|
||||
*
|
||||
* Controls object color and stroke color based on controller state.
|
||||
* 根据控制器状态控制对象颜色和描边颜色
|
||||
*/
|
||||
export class GearColor extends GearBase {
|
||||
private _storage: Map<string, IColorValue> = new Map();
|
||||
private _default: IColorValue = { color: null, strokeColor: null };
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = {
|
||||
color: this.owner.getProp(EObjectPropID.Color) as number | null,
|
||||
strokeColor: this.owner.getProp(EObjectPropID.OutlineColor) as number | null
|
||||
};
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
this.owner._gearLocked = true;
|
||||
if (gv.color !== null) {
|
||||
this.owner.setProp(EObjectPropID.Color, gv.color);
|
||||
}
|
||||
if (gv.strokeColor !== null) {
|
||||
this.owner.setProp(EObjectPropID.OutlineColor, gv.strokeColor);
|
||||
}
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv: IColorValue = {
|
||||
color: this.owner.getProp(EObjectPropID.Color) as number | null,
|
||||
strokeColor: this.owner.getProp(EObjectPropID.OutlineColor) as number | null
|
||||
};
|
||||
|
||||
this._storage.set(this._controller.selectedPageId, gv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status from buffer
|
||||
* 从缓冲区添加状态
|
||||
*/
|
||||
public addStatus(pageId: string | null, color: number | null, strokeColor: number | null): void {
|
||||
if (pageId === null) {
|
||||
this._default.color = color;
|
||||
this._default.strokeColor = strokeColor;
|
||||
} else {
|
||||
this._storage.set(pageId, { color, strokeColor });
|
||||
}
|
||||
}
|
||||
}
|
||||
71
packages/fairygui/src/gears/GearDisplay.ts
Normal file
71
packages/fairygui/src/gears/GearDisplay.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* GearDisplay
|
||||
*
|
||||
* Controls object visibility based on controller state.
|
||||
* 根据控制器状态控制对象可见性
|
||||
*/
|
||||
export class GearDisplay extends GearBase {
|
||||
/** Pages where object is visible | 对象可见的页面列表 */
|
||||
public pages: string[] = [];
|
||||
|
||||
private _visible: number = 0;
|
||||
private _displayLockToken: number = 1;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this.pages = [];
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
this._displayLockToken++;
|
||||
if (this._displayLockToken === 0) {
|
||||
this._displayLockToken = 1;
|
||||
}
|
||||
|
||||
if (
|
||||
this.pages.length === 0 ||
|
||||
(this._controller && this.pages.indexOf(this._controller.selectedPageId) !== -1)
|
||||
) {
|
||||
this._visible = 1;
|
||||
} else {
|
||||
this._visible = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
// GearDisplay doesn't need to save state
|
||||
}
|
||||
|
||||
/**
|
||||
* Add display lock
|
||||
* 添加显示锁
|
||||
*/
|
||||
public addLock(): number {
|
||||
this._visible++;
|
||||
return this._displayLockToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release display lock
|
||||
* 释放显示锁
|
||||
*/
|
||||
public releaseLock(token: number): void {
|
||||
if (token === this._displayLockToken) {
|
||||
this._visible--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if object should be visible
|
||||
* 检查对象是否应该可见
|
||||
*/
|
||||
public override get connected(): boolean {
|
||||
return this._controller === null || this._visible > 0;
|
||||
}
|
||||
}
|
||||
67
packages/fairygui/src/gears/GearDisplay2.ts
Normal file
67
packages/fairygui/src/gears/GearDisplay2.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* GearDisplay2
|
||||
*
|
||||
* Advanced display control that combines multiple controllers.
|
||||
* 高级显示控制,组合多个控制器
|
||||
*/
|
||||
export class GearDisplay2 extends GearBase {
|
||||
/** Pages where object is visible | 对象可见的页面列表 */
|
||||
public pages: string[] = [];
|
||||
|
||||
/** Condition: 0=AND, 1=OR | 条件:0=与,1=或 */
|
||||
public condition: number = 0;
|
||||
|
||||
private _visible: number = 0;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this.pages = [];
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (
|
||||
this.pages.length === 0 ||
|
||||
(this._controller && this.pages.indexOf(this._controller.selectedPageId) !== -1)
|
||||
) {
|
||||
this._visible = 1;
|
||||
} else {
|
||||
this._visible = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
// GearDisplay2 doesn't need to save state
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate visibility with condition
|
||||
* 根据条件评估可见性
|
||||
*/
|
||||
public evaluate(bConnected: boolean): boolean {
|
||||
if (this._controller === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.condition === 0) {
|
||||
// AND condition
|
||||
return bConnected && this._visible > 0;
|
||||
} else {
|
||||
// OR condition
|
||||
return bConnected || this._visible > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if object should be visible
|
||||
* 检查对象是否应该可见
|
||||
*/
|
||||
public override get connected(): boolean {
|
||||
return this._controller === null || this._visible > 0;
|
||||
}
|
||||
}
|
||||
53
packages/fairygui/src/gears/GearFontSize.ts
Normal file
53
packages/fairygui/src/gears/GearFontSize.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { EObjectPropID } from '../core/FieldTypes';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* GearFontSize
|
||||
*
|
||||
* Controls object font size based on controller state.
|
||||
* 根据控制器状态控制对象字体大小
|
||||
*/
|
||||
export class GearFontSize extends GearBase {
|
||||
private _storage: Map<string, number> = new Map();
|
||||
private _default: number = 12;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = (this.owner.getProp(EObjectPropID.FontSize) as number) ?? 12;
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const fontSize = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.setProp(EObjectPropID.FontSize, fontSize);
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
this._storage.set(
|
||||
this._controller.selectedPageId,
|
||||
(this.owner.getProp(EObjectPropID.FontSize) as number) ?? 12
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status
|
||||
* 添加状态
|
||||
*/
|
||||
public addStatus(pageId: string | null, fontSize: number): void {
|
||||
if (pageId === null) {
|
||||
this._default = fontSize;
|
||||
} else {
|
||||
this._storage.set(pageId, fontSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
packages/fairygui/src/gears/GearIcon.ts
Normal file
49
packages/fairygui/src/gears/GearIcon.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* GearIcon
|
||||
*
|
||||
* Controls object icon based on controller state.
|
||||
* 根据控制器状态控制对象图标
|
||||
*/
|
||||
export class GearIcon extends GearBase {
|
||||
private _storage: Map<string, string> = new Map();
|
||||
private _default: string = '';
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = this.owner.icon ?? '';
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const icon = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.icon = icon;
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
this._storage.set(this._controller.selectedPageId, this.owner.icon ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status
|
||||
* 添加状态
|
||||
*/
|
||||
public addStatus(pageId: string | null, icon: string): void {
|
||||
if (pageId === null) {
|
||||
this._default = icon;
|
||||
} else {
|
||||
this._storage.set(pageId, icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
packages/fairygui/src/gears/GearLook.ts
Normal file
122
packages/fairygui/src/gears/GearLook.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { GTween } from '../tween/GTween';
|
||||
import type { GTweener } from '../tween/GTweener';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* Look value for GearLook
|
||||
* GearLook 的外观值
|
||||
*/
|
||||
interface ILookValue {
|
||||
alpha: number;
|
||||
rotation: number;
|
||||
grayed: boolean;
|
||||
touchable: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* GearLook
|
||||
*
|
||||
* Controls object appearance (alpha, rotation, grayed, touchable) based on controller state.
|
||||
* 根据控制器状态控制对象外观(透明度、旋转、灰度、可触摸)
|
||||
*/
|
||||
export class GearLook extends GearBase {
|
||||
private _storage: Map<string, ILookValue> = new Map();
|
||||
private _default: ILookValue = { alpha: 1, rotation: 0, grayed: false, touchable: true };
|
||||
private _tweener: GTweener | null = null;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = {
|
||||
alpha: this.owner.alpha,
|
||||
rotation: this.owner.rotation,
|
||||
grayed: this.owner.grayed,
|
||||
touchable: this.owner.touchable
|
||||
};
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
// grayed and touchable cannot be tweened, apply immediately
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.grayed = gv.grayed;
|
||||
this.owner.touchable = gv.touchable;
|
||||
this.owner._gearLocked = false;
|
||||
|
||||
if (this.tweenConfig?.tween && this.owner.onStage) {
|
||||
if (this._tweener) {
|
||||
if (this._tweener.endValue.x !== gv.alpha || this._tweener.endValue.y !== gv.rotation) {
|
||||
this._tweener.kill();
|
||||
this._tweener = null;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const oa = this.owner.alpha;
|
||||
const or = this.owner.rotation;
|
||||
|
||||
if (oa !== gv.alpha || or !== gv.rotation) {
|
||||
this._tweener = GTween.to2(oa, or, gv.alpha, gv.rotation, this.tweenConfig.duration)
|
||||
.setDelay(this.tweenConfig.delay)
|
||||
.setEase(this.tweenConfig.easeType)
|
||||
.setTarget(this, 'look')
|
||||
.onUpdate((tweener) => {
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.alpha = tweener.value.x;
|
||||
this.owner.rotation = tweener.value.y;
|
||||
this.owner._gearLocked = false;
|
||||
})
|
||||
.onComplete(() => {
|
||||
this._tweener = null;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.alpha = gv.alpha;
|
||||
this.owner.rotation = gv.rotation;
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv: ILookValue = {
|
||||
alpha: this.owner.alpha,
|
||||
rotation: this.owner.rotation,
|
||||
grayed: this.owner.grayed,
|
||||
touchable: this.owner.touchable
|
||||
};
|
||||
|
||||
this._storage.set(this._controller.selectedPageId, gv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status from buffer
|
||||
* 从缓冲区添加状态
|
||||
*/
|
||||
public addStatus(
|
||||
pageId: string | null,
|
||||
alpha: number,
|
||||
rotation: number,
|
||||
grayed: boolean,
|
||||
touchable: boolean
|
||||
): void {
|
||||
if (pageId === null) {
|
||||
this._default.alpha = alpha;
|
||||
this._default.rotation = rotation;
|
||||
this._default.grayed = grayed;
|
||||
this._default.touchable = touchable;
|
||||
} else {
|
||||
this._storage.set(pageId, { alpha, rotation, grayed, touchable });
|
||||
}
|
||||
}
|
||||
}
|
||||
150
packages/fairygui/src/gears/GearSize.ts
Normal file
150
packages/fairygui/src/gears/GearSize.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { GTween } from '../tween/GTween';
|
||||
import type { GTweener } from '../tween/GTweener';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* Size value for GearSize
|
||||
* GearSize 的尺寸值
|
||||
*/
|
||||
interface ISizeValue {
|
||||
width: number;
|
||||
height: number;
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* GearSize
|
||||
*
|
||||
* Controls object size and scale based on controller state.
|
||||
* 根据控制器状态控制对象尺寸和缩放
|
||||
*/
|
||||
export class GearSize extends GearBase {
|
||||
private _storage: Map<string, ISizeValue> = new Map();
|
||||
private _default: ISizeValue = { width: 0, height: 0, scaleX: 1, scaleY: 1 };
|
||||
private _tweener: GTweener | null = null;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = {
|
||||
width: this.owner.width,
|
||||
height: this.owner.height,
|
||||
scaleX: this.owner.scaleX,
|
||||
scaleY: this.owner.scaleY
|
||||
};
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
if (this.tweenConfig?.tween && this.owner.onStage) {
|
||||
if (this._tweener) {
|
||||
if (
|
||||
this._tweener.endValue.x !== gv.width ||
|
||||
this._tweener.endValue.y !== gv.height ||
|
||||
this._tweener.endValue.z !== gv.scaleX ||
|
||||
this._tweener.endValue.w !== gv.scaleY
|
||||
) {
|
||||
this._tweener.kill();
|
||||
this._tweener = null;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const ow = this.owner.width;
|
||||
const oh = this.owner.height;
|
||||
const osx = this.owner.scaleX;
|
||||
const osy = this.owner.scaleY;
|
||||
|
||||
if (ow !== gv.width || oh !== gv.height || osx !== gv.scaleX || osy !== gv.scaleY) {
|
||||
this._tweener = GTween.to4(
|
||||
ow,
|
||||
oh,
|
||||
osx,
|
||||
osy,
|
||||
gv.width,
|
||||
gv.height,
|
||||
gv.scaleX,
|
||||
gv.scaleY,
|
||||
this.tweenConfig.duration
|
||||
)
|
||||
.setDelay(this.tweenConfig.delay)
|
||||
.setEase(this.tweenConfig.easeType)
|
||||
.setTarget(this, 'size')
|
||||
.onUpdate((tweener) => {
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.setSize(tweener.value.x, tweener.value.y);
|
||||
this.owner.setScale(tweener.value.z, tweener.value.w);
|
||||
this.owner._gearLocked = false;
|
||||
})
|
||||
.onComplete(() => {
|
||||
this._tweener = null;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.setSize(gv.width, gv.height);
|
||||
this.owner.setScale(gv.scaleX, gv.scaleY);
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv: ISizeValue = {
|
||||
width: this.owner.width,
|
||||
height: this.owner.height,
|
||||
scaleX: this.owner.scaleX,
|
||||
scaleY: this.owner.scaleY
|
||||
};
|
||||
|
||||
this._storage.set(this._controller.selectedPageId, gv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update size from relation changes
|
||||
* 从关联变更中更新尺寸
|
||||
*/
|
||||
public updateFromRelations(dWidth: number, dHeight: number): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
for (const gv of this._storage.values()) {
|
||||
gv.width += dWidth;
|
||||
gv.height += dHeight;
|
||||
}
|
||||
this._default.width += dWidth;
|
||||
this._default.height += dHeight;
|
||||
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status from buffer
|
||||
* 从缓冲区添加状态
|
||||
*/
|
||||
public addStatus(
|
||||
pageId: string | null,
|
||||
width: number,
|
||||
height: number,
|
||||
scaleX: number,
|
||||
scaleY: number
|
||||
): void {
|
||||
if (pageId === null) {
|
||||
this._default.width = width;
|
||||
this._default.height = height;
|
||||
this._default.scaleX = scaleX;
|
||||
this._default.scaleY = scaleY;
|
||||
} else {
|
||||
this._storage.set(pageId, { width, height, scaleX, scaleY });
|
||||
}
|
||||
}
|
||||
}
|
||||
50
packages/fairygui/src/gears/GearText.ts
Normal file
50
packages/fairygui/src/gears/GearText.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { EObjectPropID } from '../core/FieldTypes';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* GearText
|
||||
*
|
||||
* Controls object text content based on controller state.
|
||||
* 根据控制器状态控制对象文本内容
|
||||
*/
|
||||
export class GearText extends GearBase {
|
||||
private _storage: Map<string, string> = new Map();
|
||||
private _default: string = '';
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
this._default = this.owner.text ?? '';
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const text = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.text = text;
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
this._storage.set(this._controller.selectedPageId, this.owner.text ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status
|
||||
* 添加状态
|
||||
*/
|
||||
public addStatus(pageId: string | null, text: string): void {
|
||||
if (pageId === null) {
|
||||
this._default = text;
|
||||
} else {
|
||||
this._storage.set(pageId, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
159
packages/fairygui/src/gears/GearXY.ts
Normal file
159
packages/fairygui/src/gears/GearXY.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { GearBase } from './GearBase';
|
||||
import { GTween } from '../tween/GTween';
|
||||
import type { GTweener } from '../tween/GTweener';
|
||||
import type { GObject } from '../core/GObject';
|
||||
|
||||
/**
|
||||
* Position value for GearXY
|
||||
* GearXY 的位置值
|
||||
*/
|
||||
interface IPositionValue {
|
||||
x: number;
|
||||
y: number;
|
||||
px: number;
|
||||
py: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* GearXY
|
||||
*
|
||||
* Controls object position based on controller state.
|
||||
* 根据控制器状态控制对象位置
|
||||
*/
|
||||
export class GearXY extends GearBase {
|
||||
/** Use percent positions | 使用百分比位置 */
|
||||
public positionsInPercent: boolean = false;
|
||||
|
||||
private _storage: Map<string, IPositionValue> = new Map();
|
||||
private _default: IPositionValue = { x: 0, y: 0, px: 0, py: 0 };
|
||||
private _tweener: GTweener | null = null;
|
||||
|
||||
constructor(owner: GObject) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
protected init(): void {
|
||||
const parent = this.owner.parent;
|
||||
this._default = {
|
||||
x: this.owner.x,
|
||||
y: this.owner.y,
|
||||
px: parent ? this.owner.x / parent.width : 0,
|
||||
py: parent ? this.owner.y / parent.height : 0
|
||||
};
|
||||
this._storage.clear();
|
||||
}
|
||||
|
||||
public apply(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const gv = this._storage.get(this._controller.selectedPageId) ?? this._default;
|
||||
const parent = this.owner.parent;
|
||||
|
||||
let ex: number;
|
||||
let ey: number;
|
||||
|
||||
if (this.positionsInPercent && parent) {
|
||||
ex = gv.px * parent.width;
|
||||
ey = gv.py * parent.height;
|
||||
} else {
|
||||
ex = gv.x;
|
||||
ey = gv.y;
|
||||
}
|
||||
|
||||
if (this.tweenConfig?.tween && this.owner.onStage) {
|
||||
if (this._tweener) {
|
||||
if (this._tweener.endValue.x !== ex || this._tweener.endValue.y !== ey) {
|
||||
this._tweener.kill();
|
||||
this._tweener = null;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const ox = this.owner.x;
|
||||
const oy = this.owner.y;
|
||||
if (ox !== ex || oy !== ey) {
|
||||
this._tweener = GTween.to2(ox, oy, ex, ey, this.tweenConfig.duration)
|
||||
.setDelay(this.tweenConfig.delay)
|
||||
.setEase(this.tweenConfig.easeType)
|
||||
.setTarget(this, 'xy')
|
||||
.onUpdate((tweener) => {
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.setXY(tweener.value.x, tweener.value.y);
|
||||
this.owner._gearLocked = false;
|
||||
})
|
||||
.onComplete(() => {
|
||||
this._tweener = null;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.owner._gearLocked = true;
|
||||
this.owner.setXY(ex, ey);
|
||||
this.owner._gearLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public updateState(): void {
|
||||
if (!this._controller) return;
|
||||
|
||||
const parent = this.owner.parent;
|
||||
const gv: IPositionValue = {
|
||||
x: this.owner.x,
|
||||
y: this.owner.y,
|
||||
px: parent ? this.owner.x / parent.width : 0,
|
||||
py: parent ? this.owner.y / parent.height : 0
|
||||
};
|
||||
|
||||
this._storage.set(this._controller.selectedPageId, gv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update positions from relation changes
|
||||
* 从关联变更中更新位置
|
||||
*/
|
||||
public updateFromRelations(dx: number, dy: number): void {
|
||||
if (!this._controller || this.positionsInPercent) return;
|
||||
|
||||
for (const gv of this._storage.values()) {
|
||||
gv.x += dx;
|
||||
gv.y += dy;
|
||||
}
|
||||
this._default.x += dx;
|
||||
this._default.y += dy;
|
||||
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add status from buffer
|
||||
* 从缓冲区添加状态
|
||||
*/
|
||||
public addStatus(pageId: string | null, x: number, y: number): void {
|
||||
if (pageId === null) {
|
||||
this._default.x = x;
|
||||
this._default.y = y;
|
||||
} else {
|
||||
const gv = this._storage.get(pageId) ?? { x: 0, y: 0, px: 0, py: 0 };
|
||||
gv.x = x;
|
||||
gv.y = y;
|
||||
this._storage.set(pageId, gv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extended status (percent values)
|
||||
* 添加扩展状态(百分比值)
|
||||
*/
|
||||
public addExtStatus(pageId: string | null, px: number, py: number): void {
|
||||
if (pageId === null) {
|
||||
this._default.px = px;
|
||||
this._default.py = py;
|
||||
} else {
|
||||
const gv = this._storage.get(pageId);
|
||||
if (gv) {
|
||||
gv.px = px;
|
||||
gv.py = py;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
packages/fairygui/src/gears/index.ts
Normal file
11
packages/fairygui/src/gears/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export { GearBase, GearTweenConfig } from './GearBase';
|
||||
export { GearDisplay } from './GearDisplay';
|
||||
export { GearDisplay2 } from './GearDisplay2';
|
||||
export { GearXY } from './GearXY';
|
||||
export { GearSize } from './GearSize';
|
||||
export { GearLook } from './GearLook';
|
||||
export { GearColor } from './GearColor';
|
||||
export { GearText } from './GearText';
|
||||
export { GearIcon } from './GearIcon';
|
||||
export { GearFontSize } from './GearFontSize';
|
||||
export { GearAnimation } from './GearAnimation';
|
||||
Reference in New Issue
Block a user