mirror of
https://github.com/Gongxh0901/kunpolibrary
synced 2025-10-10 09:05:47 +00:00
first commit
This commit is contained in:
33
src/ui/ComponentExtendHelper.ts
Normal file
33
src/ui/ComponentExtendHelper.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-26
|
||||
* @Description: 自定义组件扩展帮助类
|
||||
*/
|
||||
import { UIObjectFactory } from "fairygui-cc";
|
||||
import { debug } from "../tool/log";
|
||||
import { PropsHelper } from "./PropsHelper";
|
||||
import { _uidecorator } from "./UIDecorator";
|
||||
|
||||
export class ComponentExtendHelper {
|
||||
public static register(): void {
|
||||
for (const { ctor, res } of _uidecorator.getComponentMaps().values()) {
|
||||
debug(`自定义组件注册 组件名:${res.name} 包名:${res.pkg}`);
|
||||
this.registerComponent(ctor, res.pkg, res.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册自定义组件信息
|
||||
* @param info
|
||||
*/
|
||||
private static registerComponent(ctor: any, pkg: string, name: string): void {
|
||||
// 自定义组件扩展
|
||||
const onConstruct = function (this: any): void {
|
||||
PropsHelper.serializeProps(this, pkg);
|
||||
this.onInit && this.onInit();
|
||||
};
|
||||
ctor.prototype.onConstruct = onConstruct;
|
||||
// 自定义组件扩展
|
||||
UIObjectFactory.setExtension(`ui://${pkg}/${name}`, ctor);
|
||||
}
|
||||
}
|
79
src/ui/IWindow.ts
Normal file
79
src/ui/IWindow.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-08
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
import { AdapterType, WindowType } from "./header";
|
||||
import { IWindowHeader } from "./IWindowHeader";
|
||||
import { WindowHeaderInfo } from "./WindowHeaderInfo";
|
||||
|
||||
export interface IWindow {
|
||||
/** 窗口类型 */
|
||||
type: WindowType;
|
||||
/** 窗口适配类型 */
|
||||
adapterType: AdapterType;
|
||||
/** 底部遮罩的透明度 */
|
||||
bgAlpha: number;
|
||||
/**
|
||||
* 窗口适配 (框架内部使用)
|
||||
*/
|
||||
_adapted(): void;
|
||||
|
||||
/**
|
||||
* 初始化方法 (框架内部使用)
|
||||
* @param swallowTouch 是否吞噬触摸事件
|
||||
*/
|
||||
_init(swallowTouch: boolean, bgAlpha: number): void;
|
||||
/**
|
||||
* 窗口关闭 (框架内部使用)
|
||||
*/
|
||||
_close(): void;
|
||||
/**
|
||||
* 显示窗口 (框架内部使用)
|
||||
* @param userdata 用户自定义数据
|
||||
*/
|
||||
_show(userdata?: any): void;
|
||||
/**
|
||||
* 从隐藏状态恢复显示
|
||||
*/
|
||||
_showFromHide(): void;
|
||||
/**
|
||||
* 隐藏窗口 (框架内部使用)
|
||||
*/
|
||||
_hide(): void;
|
||||
/**
|
||||
* 窗口被遮挡 被同组或者不同组的其他窗口覆盖 (框架内部使用)
|
||||
*/
|
||||
_cover(): void;
|
||||
/**
|
||||
* 恢复窗口遮挡 被同组或者不同组的其他窗口覆盖恢复 (框架内部使用)
|
||||
*/
|
||||
_recover(): void;
|
||||
|
||||
/**
|
||||
* 调整窗口的显示层级
|
||||
* @param depth
|
||||
*/
|
||||
_setDepth(depth: number): void;
|
||||
|
||||
/**
|
||||
* 窗口是否显示
|
||||
*/
|
||||
isShowing(): boolean;
|
||||
|
||||
/**
|
||||
* 窗口是否被遮挡了
|
||||
*/
|
||||
isCover(): boolean;
|
||||
|
||||
/**
|
||||
* 窗口尺寸发生改变时被调用
|
||||
*/
|
||||
screenResize(): void;
|
||||
|
||||
/** 获取资源栏数据 */
|
||||
getHeaderInfo(): WindowHeaderInfo;
|
||||
|
||||
setHeader(header: IWindowHeader): void;
|
||||
}
|
45
src/ui/IWindowHeader.ts
Normal file
45
src/ui/IWindowHeader.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { AdapterType } from "./header";
|
||||
import { IWindow } from "./IWindow";
|
||||
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-08
|
||||
* @Description: 窗口顶边资源栏
|
||||
*/
|
||||
export interface IWindowHeader {
|
||||
/** 资源栏名称 */
|
||||
name: string;
|
||||
/** 窗口适配类型 */
|
||||
adapterType: AdapterType;
|
||||
/** 引用计数 */
|
||||
_refCount: number;
|
||||
/**
|
||||
* 初始化 (内部方法)
|
||||
*/
|
||||
_init(): void;
|
||||
/**
|
||||
* 窗口适配 (内部方法)
|
||||
*/
|
||||
_adapted(): void;
|
||||
/**
|
||||
* 显示 (内部方法)
|
||||
* @param {IWindow} window 所属窗口
|
||||
*/
|
||||
_show(window: IWindow): void;
|
||||
/**
|
||||
* 隐藏 (内部方法)
|
||||
*/
|
||||
_hide(): void;
|
||||
/**
|
||||
* 关闭 (内部方法)
|
||||
*/
|
||||
_close(): void;
|
||||
|
||||
/** 增加引用计数 (内部方法) */
|
||||
_addRef(): void;
|
||||
/** 减少引用计数 (内部方法) */
|
||||
_decRef(): number;
|
||||
|
||||
/** 屏幕大小改变时被调用 (内部方法) */
|
||||
_screenResize(): void;
|
||||
}
|
89
src/ui/PropsHelper.ts
Normal file
89
src/ui/PropsHelper.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2025-01-09
|
||||
* @Description: 属性辅助类
|
||||
*/
|
||||
|
||||
import { GComponent } from "fairygui-cc";
|
||||
import { warn } from "../tool/log";
|
||||
|
||||
interface IPropsConfig {
|
||||
[packageName: string]: { [componentName: string]: IPropsInfo };
|
||||
}
|
||||
|
||||
interface IPropsInfo {
|
||||
props: (string | number)[];
|
||||
callbacks: (string | number)[];
|
||||
}
|
||||
|
||||
export class PropsHelper {
|
||||
private static _config: IPropsConfig = {};
|
||||
public static setConfig(config: IPropsConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
/** 序列化属性 */
|
||||
public static serializeProps(component: GComponent, packageName: string): void {
|
||||
if (!this._config) {
|
||||
return;
|
||||
}
|
||||
const config = this._config[packageName];
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const componentName = component.name;
|
||||
const propsInfo = config[componentName];
|
||||
if (!propsInfo) {
|
||||
return;
|
||||
}
|
||||
// 设置属性
|
||||
const props = propsInfo.props;
|
||||
this.serializationPropsNode(component, props);
|
||||
|
||||
// 设置回调
|
||||
const callbacks = propsInfo.callbacks;
|
||||
this.serializationCallbacksNode(component, callbacks);
|
||||
}
|
||||
|
||||
/** 给界面中定义的属性赋值 */
|
||||
private static serializationPropsNode(component: GComponent, props: (string | number)[]) {
|
||||
const propsCount = props.length;
|
||||
// [name1, len, ...props1, name2, len, ...props2, ...]
|
||||
let index = 0;
|
||||
while (index < propsCount) {
|
||||
const propName = props[index++] as string;
|
||||
const endIndex = index + (props[index] as number);
|
||||
let uinode = component;
|
||||
while (++index <= endIndex) {
|
||||
uinode = uinode.getChildAt(props[index] as number);
|
||||
if (!uinode) {
|
||||
warn(`无法对UI类(${component.name})属性(${propName})赋值,请检查节点配置是否正确`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
(component as any)[propName] = (uinode == component ? null : uinode);
|
||||
}
|
||||
}
|
||||
|
||||
private static serializationCallbacksNode(component: GComponent, callbacks: (string | number)[]) {
|
||||
const propsCount = callbacks.length;
|
||||
// [name1, len, ...props1, name2, len, ...props2, ...]
|
||||
let index = 0;
|
||||
while (index < propsCount) {
|
||||
const propName = callbacks[index++] as string;
|
||||
const endIndex = index + (callbacks[index] as number);
|
||||
let uinode = component;
|
||||
while (++index <= endIndex) {
|
||||
uinode = uinode.getChildAt(callbacks[index] as number);
|
||||
if (!uinode) {
|
||||
warn(`无法对UI类(${component.name})的(${propName})设置回调,请检查节点配置是否正确`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uinode != component) {
|
||||
uinode.onClick((component as any)[propName], component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
180
src/ui/UIDecorator.ts
Normal file
180
src/ui/UIDecorator.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-11
|
||||
* @Description: UI 装饰器
|
||||
*/
|
||||
|
||||
import { ObjectHelper } from "../tool/helper/ObjectHelper";
|
||||
export namespace _uidecorator {
|
||||
const UIPropMeta = "__uipropmeta__"
|
||||
const UICBMeta = "__uicbmeta__"
|
||||
|
||||
interface IUIInfoBase {
|
||||
/** 构造函数 */
|
||||
ctor: any;
|
||||
/** 属性 */
|
||||
props: Record<string, 1>;
|
||||
/** 方法 */
|
||||
callbacks: Record<string, Function>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 窗口属性注册数据结构
|
||||
*/
|
||||
interface UIWindowInfo extends IUIInfoBase {
|
||||
/** 配置信息 */
|
||||
res: {
|
||||
/** 窗口组名称 */
|
||||
group: string;
|
||||
/** fgui包名 */
|
||||
pkg: string;
|
||||
/** 窗口名 */
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
/** 用来存储窗口注册信息 */
|
||||
const uiclassMap: Map<any, UIWindowInfo> = new Map();
|
||||
|
||||
/** 获取窗口注册信息 */
|
||||
export function getWindowMaps(): Map<any, UIWindowInfo> {
|
||||
return uiclassMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 窗口装饰器
|
||||
* @param {string} groupName 窗口组名称
|
||||
* @param {string} pkgName fgui包名
|
||||
* @param {string} name 窗口名 (与fgui中的组件名一一对应)
|
||||
*/
|
||||
export function uiclass(groupName: string, pkgName: string, name: string): Function {
|
||||
/** target 类的构造函数 */
|
||||
return function (ctor: any): void {
|
||||
// debug(`uiclass >${JSON.stringify(res)}<`);
|
||||
// debug(`uiclass prop >${JSON.stringify(ctor[UIPropMeta] || {})}<`);
|
||||
uiclassMap.set(ctor, {
|
||||
ctor: ctor,
|
||||
props: ctor[UIPropMeta] || null,
|
||||
callbacks: ctor[UICBMeta] || null,
|
||||
res: {
|
||||
group: groupName,
|
||||
pkg: pkgName,
|
||||
name: name,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 组件属性注册数据结构
|
||||
*/
|
||||
interface IUIComInfo extends IUIInfoBase {
|
||||
/** 配置信息 */
|
||||
res: {
|
||||
/** fgui包名 */
|
||||
pkg: string;
|
||||
/** 组件名 */
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
/** 用来存储组件注册信息 */
|
||||
let uicomponentMap: Map<string, IUIComInfo> = new Map();
|
||||
|
||||
/** 获取组件注册信息 */
|
||||
export function getComponentMaps(): Map<any, IUIComInfo> {
|
||||
return uicomponentMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* UI组件装饰器
|
||||
* @param {string} pkg 包名
|
||||
* @param {string} name 组件名
|
||||
*/
|
||||
export function uicom(pkg: string, name: string): Function {
|
||||
return function (ctor: any): void {
|
||||
// log(`pkg:【${pkg}】 uicom prop >${JSON.stringify(ctor[UIPropMeta] || {})}<`);
|
||||
uicomponentMap.set(ctor, {
|
||||
ctor: ctor,
|
||||
props: ctor[UIPropMeta] || null,
|
||||
callbacks: ctor[UICBMeta] || null,
|
||||
res: {
|
||||
pkg: pkg,
|
||||
name: name,
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* header属性注册数据结构
|
||||
*/
|
||||
interface IUIHeaderInfo extends IUIInfoBase {
|
||||
/** 配置信息 */
|
||||
res: {
|
||||
/** fgui包名 */
|
||||
pkg: string;
|
||||
/** 组件名 */
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
/** 用来存储组件注册信息 */
|
||||
let uiheaderMap: Map<string, IUIHeaderInfo> = new Map();
|
||||
|
||||
/** 获取header注册信息 */
|
||||
export function getHeaderMaps(): Map<any, IUIHeaderInfo> {
|
||||
return uiheaderMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* UI header装饰器
|
||||
* @param {string} pkg 包名
|
||||
* @param {string} name 组件名
|
||||
*/
|
||||
export function uiheader(pkg: string, name: string): Function {
|
||||
return function (ctor: any): void {
|
||||
// log(`pkg:【${pkg}】 uiheader prop >${JSON.stringify(ctor[UIPropMeta] || {})}<`);
|
||||
uiheaderMap.set(ctor, {
|
||||
ctor: ctor,
|
||||
props: ctor[UIPropMeta] || null,
|
||||
callbacks: ctor[UICBMeta] || null,
|
||||
res: {
|
||||
pkg: pkg,
|
||||
name: name,
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* UI属性装饰器
|
||||
* @param {Object} target 实例成员的类的原型
|
||||
* @param {string} name 属性名
|
||||
*
|
||||
* example: @uiprop node: GObject
|
||||
*/
|
||||
export function uiprop(target: Object, name: string): any {
|
||||
// debug("属性装饰器:", target.constructor, name);
|
||||
ObjectHelper.getObjectProp(target.constructor, UIPropMeta)[name] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法装饰器 (给点击事件用)
|
||||
* @param {Object} target 实例成员的类的原型
|
||||
* @param {string} name 方法名
|
||||
*/
|
||||
export function uiclick(target: Object, name: string, descriptor: PropertyDescriptor): void {
|
||||
// debug("方法装饰器:", target.constructor, name, descriptor);
|
||||
ObjectHelper.getObjectProp(target.constructor, UICBMeta)[name] = descriptor.value;
|
||||
}
|
||||
}
|
||||
|
||||
let _global = globalThis || window || global;
|
||||
(_global as any)["getKunpoRegisterWindowMaps"] = function () {
|
||||
return _uidecorator.getWindowMaps() as any;
|
||||
};
|
||||
(_global as any)["getKunpoRegisterComponentMaps"] = function () {
|
||||
return _uidecorator.getComponentMaps() as any;
|
||||
};
|
||||
(_global as any)["getKunpoRegisterHeaderMaps"] = function () {
|
||||
return _uidecorator.getHeaderMaps() as any;
|
||||
};
|
393
src/ui/WindowGroup.ts
Normal file
393
src/ui/WindowGroup.ts
Normal file
@@ -0,0 +1,393 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-08
|
||||
* @Description: 窗口组 (在同一个窗口容器的上的窗口)
|
||||
*/
|
||||
|
||||
import { Color, warn } from "cc";
|
||||
import { GComponent, GGraph, UIPackage } from "fairygui-cc";
|
||||
import { WindowBase } from "../fgui/WindowBase";
|
||||
import { WindowHeader } from "../fgui/WindowHeader";
|
||||
import { Screen } from "../global/Screen";
|
||||
import { WindowType } from "./header";
|
||||
import { IWindow } from "./IWindow";
|
||||
import { PropsHelper } from "./PropsHelper";
|
||||
import { WindowManager } from "./WindowManager";
|
||||
import { WindowInfo } from "./WindowResPool";
|
||||
|
||||
export class WindowGroup {
|
||||
/** 窗口组的名字 */
|
||||
private _name: string = "";
|
||||
/** 窗口组的根节点 */
|
||||
private _root: GComponent;
|
||||
/** 忽略顶部窗口查询 */
|
||||
private _ignoreQuery: boolean = false;
|
||||
/** 吞噬触摸事件 */
|
||||
private _swallowTouch: boolean = false;
|
||||
/** 窗口容器中的窗口名列表 */
|
||||
private _windowNames: string[] = [];
|
||||
/** 窗口顶部资源栏 */
|
||||
private _headers: Map<string, WindowHeader> = new Map();
|
||||
/** 半透明遮罩的透明度 */
|
||||
private _bgAlpha: number = 0;
|
||||
/** 半透明节点 */
|
||||
private _alphaGraph: GGraph;
|
||||
/** 半透明遮罩的颜色 */
|
||||
private _color: Color = new Color(0, 0, 0, 255);
|
||||
|
||||
/**
|
||||
* 获取窗口组的名称。
|
||||
* @returns {string} 窗口组的名称。
|
||||
*/
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前窗口组中窗口的数量。
|
||||
* @returns 窗口数量
|
||||
*/
|
||||
public get size(): number {
|
||||
return this._windowNames.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否忽略查询的状态。
|
||||
* @returns {boolean} 如果忽略查询,则返回 true,否则返回 false。
|
||||
*/
|
||||
public get isIgnore(): boolean {
|
||||
return this._ignoreQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化
|
||||
* @param name 组名
|
||||
* @param root 窗口组的根节点 一个fgui的组件
|
||||
* @param ignoreQuery 是否忽略顶部窗口查询
|
||||
* @param swallowTouch 是否吞掉触摸事件
|
||||
*/
|
||||
constructor(name: string, root: GComponent, ignoreQuery: boolean, swallowTouch: boolean, bgAlpha: number) {
|
||||
this._name = name;
|
||||
this._root = root;
|
||||
this._ignoreQuery = ignoreQuery;
|
||||
this._swallowTouch = swallowTouch;
|
||||
this._bgAlpha = bgAlpha;
|
||||
|
||||
const alphaGraph = new GGraph();
|
||||
alphaGraph.touchable = false;
|
||||
alphaGraph.name = "bgAlpha";
|
||||
alphaGraph.setPosition(root.width * 0.5, root.height * 0.5);
|
||||
alphaGraph.setSize(root.width, root.height, true);
|
||||
alphaGraph.setPivot(0.5, 0.5, true);
|
||||
root.addChild(alphaGraph);
|
||||
this._alphaGraph = alphaGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据窗口名创建窗口 并添加到显示节点
|
||||
* @param windowName 窗口名
|
||||
*/
|
||||
private _createWindow(pkg: string, name: string): WindowBase {
|
||||
let window = UIPackage.createObject(pkg, name) as WindowBase;
|
||||
window.name = name;
|
||||
PropsHelper.serializeProps(window, pkg);
|
||||
window._init(this._swallowTouch, this._bgAlpha);
|
||||
window._adapted();
|
||||
this._createHeader(window);
|
||||
// 添加到显示节点
|
||||
this._addWindow(window);
|
||||
return window;
|
||||
}
|
||||
|
||||
private _addWindow(window: WindowBase): void {
|
||||
this._root.addChild(window);
|
||||
WindowManager._addWindow(window.name, window);
|
||||
}
|
||||
|
||||
public showWindow(info: WindowInfo, userdata?: any): void {
|
||||
let name = info.name;
|
||||
let window = WindowManager.getWindow(name);
|
||||
if (window) {
|
||||
window._show(userdata);
|
||||
} else {
|
||||
window = this._createWindow(info.pkg, name);
|
||||
this._processWindowCloseStatus(window);
|
||||
this._windowNames.push(name);
|
||||
window._show(userdata);
|
||||
}
|
||||
this._moveWindowToTop(name);
|
||||
// 处理header的显示
|
||||
this._processHeaderStatus();
|
||||
// 显示窗口组
|
||||
this._root.visible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定名称的窗口。
|
||||
* @param name 窗口的名称。
|
||||
*/
|
||||
public _removeWindow(name: string): void {
|
||||
let index = this._windowNames.lastIndexOf(name);
|
||||
let lastIndex = this.size - 1;
|
||||
if (index < 0) {
|
||||
warn(`窗口组${this._name}中未找到窗口${name} 删除失败`);
|
||||
return;
|
||||
}
|
||||
let window = WindowManager.getWindow<WindowBase>(name);
|
||||
let header = window.getHeader();
|
||||
header && this._removeHeader(header);
|
||||
|
||||
this._windowNames.splice(index, 1);
|
||||
|
||||
// 关闭窗口 并从窗口map中移除
|
||||
WindowManager._removeWindow(name);
|
||||
|
||||
// 处理窗口显示和隐藏状态
|
||||
this._processWindowHideStatus(this.size - 1, true);
|
||||
if (this.size == 0) {
|
||||
// 窗口组中不存在窗口时 隐藏窗口组节点
|
||||
this._root.visible = false;
|
||||
} else if (lastIndex == index && index > 0) {
|
||||
// 删除的窗口是最后一个 并且前边还有窗口 调整半透明节点的显示层级
|
||||
let topName = this.getTopWindowName();
|
||||
let window = WindowManager.getWindow(topName);
|
||||
// 调整半透明遮罩
|
||||
this._adjustAlphaGraph(window);
|
||||
// 调整窗口的显示层级
|
||||
window._setDepth(this._root.numChildren - 1);
|
||||
}
|
||||
this._processHeaderStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定名称的窗口移动到窗口组的最顶层。
|
||||
* @param name 窗口的名称。
|
||||
*/
|
||||
public _moveWindowToTop(name: string): boolean {
|
||||
let isMoved = false;
|
||||
if (this.size == 0) {
|
||||
warn(`WindowGroup.moveWindowToTop: window group 【${this._name}】 is empty`);
|
||||
return;
|
||||
}
|
||||
if (this._windowNames[this.size - 1] == name) {
|
||||
// 已经在最顶层了
|
||||
} else {
|
||||
const index = this._windowNames.indexOf(name);
|
||||
if (index == -1) {
|
||||
warn(`WindowGroup.moveWindowToTop: window 【${name}】 not found in window group 【${this._name}】`);
|
||||
return;
|
||||
}
|
||||
if (index < this._windowNames.length - 1) {
|
||||
this._windowNames.splice(index, 1);
|
||||
// 放到数组的末尾
|
||||
this._windowNames.push(name);
|
||||
isMoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
let window = WindowManager.getWindow(name);
|
||||
// 先调整半透明遮罩
|
||||
this._adjustAlphaGraph(window);
|
||||
// 再调整窗口的显示层级
|
||||
window._setDepth(this._root.numChildren - 1);
|
||||
// 处理窗口显示和隐藏状态
|
||||
this._processWindowHideStatus(this.size - 1, isMoved);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理index下层窗口的隐藏状态的私有方法。递归调用
|
||||
* @param index - 窗口索引
|
||||
* @param isRecursion - 是否递归调用
|
||||
*/
|
||||
private _processWindowHideStatus(index: number, isRecursion: boolean = true): void {
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
let windowName = this._windowNames[index];
|
||||
let curWindow = WindowManager.getWindow(windowName);
|
||||
// 如果当前是当前组中的最后一个窗口并且当前窗口是隐藏状态 则恢复隐藏
|
||||
if (index == this.size - 1 && !curWindow.isShowing()) {
|
||||
curWindow._showFromHide();
|
||||
}
|
||||
if (index == 0) {
|
||||
return;
|
||||
}
|
||||
let windowType = curWindow.type;
|
||||
if (windowType == WindowType.HideAll) {
|
||||
for (let i = index - 1; i >= 0; --i) {
|
||||
let name = this._windowNames[i];
|
||||
const window = WindowManager.getWindow(name);
|
||||
window.isShowing() && window._hide();
|
||||
}
|
||||
return;
|
||||
} else if (windowType == WindowType.HideOne) {
|
||||
// 隐藏前一个
|
||||
let prevWindowName = this._windowNames[index - 1];
|
||||
let prevWindow = WindowManager.getWindow(prevWindowName);
|
||||
prevWindow.isShowing() && prevWindow._hide();
|
||||
} else {
|
||||
// 如果前一个窗口被隐藏了 需要恢复显示
|
||||
let prevWindowName = this._windowNames[index - 1];
|
||||
let prevWindow = WindowManager.getWindow(prevWindowName);
|
||||
!prevWindow.isShowing() && prevWindow._showFromHide();
|
||||
}
|
||||
isRecursion && this._processWindowHideStatus(index - 1, isRecursion);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新创建窗口时,根据新创建的窗口类型
|
||||
* 处理上一个窗口或者所有窗口的关闭
|
||||
*/
|
||||
private _processWindowCloseStatus(window: IWindow): void {
|
||||
// 新创建窗口 如果需要关闭窗口或者关闭所有窗口 处理窗口的关闭
|
||||
if (window.type == WindowType.CloseOne) {
|
||||
let size = this.size;
|
||||
while (size > 0) {
|
||||
let name = this._windowNames.pop();
|
||||
let window = WindowManager.getWindow<WindowBase>(name);
|
||||
let header = window.getHeader();
|
||||
header && this._removeHeader(header);
|
||||
WindowManager._removeWindow(name);
|
||||
break;
|
||||
}
|
||||
} else if (window.type == WindowType.CloseAll) {
|
||||
let size = this.size;
|
||||
for (let i = size; i > 0;) {
|
||||
let name = this._windowNames[--i]
|
||||
let window = WindowManager.getWindow<WindowBase>(name);
|
||||
let header = window.getHeader();
|
||||
header && this._removeHeader(header);
|
||||
WindowManager._removeWindow(name);
|
||||
}
|
||||
this._windowNames.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理header的显示状态 并调整层级 */
|
||||
private _processHeaderStatus(): void {
|
||||
// 找到第一个要显示的header
|
||||
let firstHeader: WindowHeader = null;
|
||||
let firstWindow: IWindow = null;
|
||||
let index = this.size - 1;
|
||||
for (let i = this.size - 1; i >= 0; --i) {
|
||||
let name = this._windowNames[i];
|
||||
let window = WindowManager.getWindow<WindowBase>(name);;
|
||||
if (window.isShowing() && window.getHeader()) {
|
||||
firstWindow = window;
|
||||
firstHeader = window.getHeader();
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._headers.forEach((header, name) => {
|
||||
this._root.setChildIndex(header, 0);
|
||||
if (!firstHeader && header.visible) {
|
||||
header._hide();
|
||||
} else if (firstHeader) {
|
||||
if (firstHeader.name == name && !header.visible) {
|
||||
header._show(firstWindow);
|
||||
} else if (firstHeader.name != name && header.visible) {
|
||||
header._hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (firstHeader) {
|
||||
if (index == this.size - 1) {
|
||||
this._root.setChildIndex(firstHeader, this._root.numChildren - 1);
|
||||
} else {
|
||||
this._root.setChildIndex(firstHeader, this._root.numChildren - this.size + index - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整指定窗口的透明度图形。并根据窗口的背景透明度绘制半透明遮罩。
|
||||
* @param window - 需要调整透明度的窗口对象。
|
||||
*/
|
||||
private _adjustAlphaGraph(window: IWindow): void {
|
||||
this._root.setChildIndex(this._alphaGraph, this._root.numChildren - 1);
|
||||
|
||||
// 半透明遮罩绘制
|
||||
this._color.a = window.bgAlpha * 255;
|
||||
this._alphaGraph.clearGraphics();
|
||||
this._alphaGraph.drawRect(0, this._color, this._color);
|
||||
}
|
||||
|
||||
public hasWindow(name: string): boolean {
|
||||
return this._windowNames.indexOf(name) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取窗口组中顶部窗口的名称。
|
||||
* @returns {string} 顶部窗口的名称。
|
||||
*/
|
||||
public getTopWindowName(): string {
|
||||
if (this.size > 0) {
|
||||
return this._windowNames[this.size - 1];
|
||||
}
|
||||
warn(`WindowGroup.getTopWindowName: window group 【${this._name}】 is empty`);
|
||||
}
|
||||
|
||||
|
||||
/** 根据窗口 创建顶部资源栏 (内部方法) */
|
||||
private _createHeader(window: IWindow): void {
|
||||
// 只有创建界面的时候, 才会尝试创建顶部资源栏
|
||||
let headerInfo = window.getHeaderInfo();
|
||||
if (!headerInfo) {
|
||||
return;
|
||||
}
|
||||
let name = headerInfo.name;
|
||||
let header = this._getHeader(name);
|
||||
if (header) {
|
||||
window.setHeader(header);
|
||||
header._addRef();
|
||||
} else {
|
||||
// 创建header节点
|
||||
let { pkg } = WindowManager._getResPool().getHeader(name);
|
||||
let newHeader = UIPackage.createObject(pkg, name) as WindowHeader;
|
||||
newHeader.name = name;
|
||||
newHeader.opaque = false;
|
||||
window.setHeader(newHeader);
|
||||
newHeader.visible = false;
|
||||
PropsHelper.serializeProps(newHeader, pkg);
|
||||
newHeader._init();
|
||||
newHeader._adapted();
|
||||
this._root.addChild(newHeader);
|
||||
// 添加到显示节点
|
||||
newHeader._addRef();
|
||||
this._headers.set(newHeader.name, newHeader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶部资源栏窗口 从管理器中移除 (内部方法)
|
||||
* @param header 资源栏
|
||||
*/
|
||||
public _removeHeader(header: WindowHeader): void {
|
||||
if (this._headers.has(header.name)) {
|
||||
let refCount = header._decRef();
|
||||
if (refCount <= 0) {
|
||||
this._headers.delete(header.name);
|
||||
header._close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部资源栏 (内部方法)
|
||||
* @param name 资源栏的名称
|
||||
*/
|
||||
public _getHeader<T extends WindowHeader>(name: string): T | null {
|
||||
return this._headers.get(name) as T;
|
||||
}
|
||||
|
||||
/** 屏幕大小改变时被调用 (内部方法) */
|
||||
public _screenResize(): void {
|
||||
this._headers.forEach((header) => {
|
||||
header._screenResize();
|
||||
});
|
||||
this._alphaGraph.setPosition(Screen.ScreenWidth * 0.5, Screen.ScreenHeight * 0.5);
|
||||
this._alphaGraph.setSize(Screen.ScreenWidth, Screen.ScreenHeight, true);
|
||||
this._alphaGraph.setPivot(0.5, 0.5, true);
|
||||
}
|
||||
}
|
25
src/ui/WindowHeaderInfo.ts
Normal file
25
src/ui/WindowHeaderInfo.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2025-01-10
|
||||
* @Description: 窗口顶部资源栏信息
|
||||
*/
|
||||
|
||||
export class WindowHeaderInfo {
|
||||
/** header名字 */
|
||||
name: string;
|
||||
/** 自定义数据 用于Header窗口 onShow方法的自定义参数 */
|
||||
userdata: any;
|
||||
|
||||
/**
|
||||
* 创建 WindowHeaderInfo
|
||||
* @param {string} name header窗口名
|
||||
* @param {*} [userdata] 自定义数据
|
||||
* @returns {WindowHeaderInfo}
|
||||
*/
|
||||
static create(name: string, userdata?: any): WindowHeaderInfo {
|
||||
const info = new WindowHeaderInfo();
|
||||
info.name = name;
|
||||
info.userdata = userdata;
|
||||
return info;
|
||||
}
|
||||
}
|
216
src/ui/WindowManager.ts
Normal file
216
src/ui/WindowManager.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-07
|
||||
* @Description: 窗口管理类
|
||||
*/
|
||||
|
||||
import { debug, warn } from "../tool/log";
|
||||
import { ComponentExtendHelper } from "./ComponentExtendHelper";
|
||||
import { IWindow } from "./IWindow";
|
||||
import { _uidecorator } from "./UIDecorator";
|
||||
import { WindowGroup } from "./WindowGroup";
|
||||
import { WindowResPool } from "./WindowResPool";
|
||||
|
||||
export class WindowManager {
|
||||
/** 窗口组 */
|
||||
private static _groups: Map<string, WindowGroup> = new Map();
|
||||
/** 不忽略查询的窗口组名 */
|
||||
private static _queryGroupNames: string[] = [];
|
||||
/** 所有窗口全部放到这个map中 */
|
||||
private static _windows: Map<string, IWindow> = new Map();
|
||||
/** 初始化时传入实例 */
|
||||
private static _resPool: WindowResPool;
|
||||
|
||||
/**
|
||||
* 打开一个窗口
|
||||
* @param windowName 窗口名
|
||||
* @param userdata 用户数据
|
||||
*/
|
||||
public static showWindow(windowName: string, userdata?: any): void {
|
||||
//TODO::如果没有资源 加载资源
|
||||
this.showWindowIm(windowName, userdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定名称的窗口,并传递可选的用户数据。
|
||||
* @param windowName - 窗口的名称。
|
||||
* @param userdata - 可选参数,用于传递给窗口的用户数据。
|
||||
*/
|
||||
public static showWindowIm(windowName: string, userdata?: any): void {
|
||||
const info = this._resPool.get(windowName);
|
||||
const windowGroup = this.getWindowGroup(info.group);
|
||||
windowGroup.showWindow(info, userdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭窗口
|
||||
* @param windowName 窗口名
|
||||
*/
|
||||
public static closeWindow(windowName: string): void {
|
||||
if (!this._windows.has(windowName)) {
|
||||
warn(`窗口不存在 ${windowName} 不需要关闭`);
|
||||
return;
|
||||
}
|
||||
// 先在窗口组中移除
|
||||
let info = this._resPool.get(windowName);
|
||||
const windowGroup = this.getWindowGroup(info.group);
|
||||
windowGroup._removeWindow(windowName);
|
||||
// 窗口组中没有窗口了
|
||||
if (windowGroup.size == 0) {
|
||||
let index = this._queryGroupNames.indexOf(windowGroup.name);
|
||||
if (index > 0 && windowGroup.name == this.getTopGroupName()) {
|
||||
do {
|
||||
const groupName = this._queryGroupNames[--index];
|
||||
let group = this.getWindowGroup(groupName);
|
||||
if (group.size > 0) {
|
||||
this.getWindow(group.getTopWindowName())._recover();
|
||||
break;
|
||||
}
|
||||
} while (index >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前最顶层的窗口实例。
|
||||
* @template T - 窗口实例的类型,必须继承自 IWindow 接口。
|
||||
* @returns {T | null} - 返回最顶层的窗口实例,如果没有找到则返回 null。
|
||||
* @description 该方法会遍历所有窗口组,找到最顶层的窗口并返回其实例。
|
||||
*/
|
||||
public static getTopWindow<T extends IWindow>(): T | null {
|
||||
let len = this._queryGroupNames.length;
|
||||
for (let i = len; i > 0;) {
|
||||
let group = this.getWindowGroup(this._queryGroupNames[--i]);
|
||||
if (group.size > 0) {
|
||||
return this.getWindow<T>(group.getTopWindowName());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据窗口名称获取窗口实例。
|
||||
* @template T 窗口类型,必须继承自IWindow接口。
|
||||
* @param name 窗口的名称。
|
||||
* @returns 如果找到窗口,则返回对应类型的窗口实例;否则返回null。
|
||||
*/
|
||||
public static getWindow<T extends IWindow>(name: string): T | null {
|
||||
return this._windows.get(name) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在指定名称的窗口。
|
||||
* @param name 窗口的名称。
|
||||
* @returns 如果存在指定名称的窗口,则返回 true,否则返回 false。
|
||||
*/
|
||||
public static hasWindow(name: string): boolean {
|
||||
return this._windows.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定的组名获取窗口组。如果组不存在,则抛出错误。
|
||||
* @param groupName 窗口组的名称。
|
||||
* @returns 返回找到的窗口组。
|
||||
*/
|
||||
public static getWindowGroup(groupName: string): WindowGroup {
|
||||
if (this._groups.has(groupName)) {
|
||||
return this._groups.get(groupName);
|
||||
}
|
||||
throw new Error(`WindowManager.getWindowGroup: window group 【${groupName}】 not found`);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前顶层窗口组的名称。
|
||||
* 返回第一个包含至少一个窗口的窗口组名称。(该方法只检查不忽略查询的窗口组)
|
||||
* 如果没有找到任何包含窗口的组,则返回空字符串。
|
||||
*/
|
||||
public static getTopGroupName(): string {
|
||||
let len = this._queryGroupNames.length;
|
||||
for (let i = len - 1; i >= 0; i--) {
|
||||
let name = this._queryGroupNames[i];
|
||||
let group = this._groups.get(name);
|
||||
if (group.size > 0) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化窗口管理器,设置资源池。 (框架内部使用)
|
||||
* @param resPool - 窗口资源池实例。
|
||||
*/
|
||||
public static _init(resPool: WindowResPool): void {
|
||||
this._resPool = resPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向窗口管理器添加一个新窗口。 (框架内部使用)
|
||||
* @param name 窗口的唯一标识符。
|
||||
* @param window 要添加的窗口对象,需实现 IWindow 接口。
|
||||
*/
|
||||
public static _addWindow(name: string, window: IWindow): void {
|
||||
this._windows.set(name, window);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定名称的窗口。 (框架内部使用)
|
||||
* @param name 窗口的名称。
|
||||
*/
|
||||
public static _removeWindow(name: string): void {
|
||||
if (this.hasWindow(name)) {
|
||||
this._windows.get(name)._close();
|
||||
this._windows.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册所有UI窗口类到资源池中。 (框架内部使用)
|
||||
* 该方法遍历所有通过_uidecorator.getWindowMaps()获取的窗口映射,
|
||||
* 并将每个窗口的资源名称、构造函数、分组和包信息添加到资源池中。
|
||||
*/
|
||||
public static registerUI(): void {
|
||||
// 窗口注册
|
||||
for (const { ctor, res } of _uidecorator.getWindowMaps().values()) {
|
||||
debug(`窗口注册 窗口名:${res.name} 包名:${res.pkg} 组名:${res.group}`);
|
||||
this._resPool.add(ctor, res.group, res.pkg, res.name);
|
||||
}
|
||||
// 窗口header注册
|
||||
for (const { ctor, res } of _uidecorator.getHeaderMaps().values()) {
|
||||
debug(`header注册 header名:${res.name} 包名:${res.pkg}`);
|
||||
this._resPool.addHeader(ctor, res.pkg, res.name);
|
||||
}
|
||||
// 组件注册
|
||||
ComponentExtendHelper.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* 向窗口管理器添加一个窗口组 如果窗口组名称已存在,则抛出错误. (内部方法)
|
||||
* @param group 要添加的窗口组
|
||||
*/
|
||||
public static _addWindowGroup(group: WindowGroup): void {
|
||||
if (this._groups.has(group.name)) {
|
||||
throw new Error(`WindowManager._addWindowGroup: window group 【${group.name}】 already exists`);
|
||||
}
|
||||
this._groups.set(group.name, group);
|
||||
// 不忽略查询 加到列表中
|
||||
!group.isIgnore && this._queryGroupNames.push(group.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 屏幕大小改变时 调用所有窗口的screenResize方法 (内部方法)
|
||||
*/
|
||||
public static _screenResize(): void {
|
||||
this._windows.forEach((window: IWindow) => {
|
||||
window.screenResize();
|
||||
});
|
||||
this._groups.forEach((group: WindowGroup) => {
|
||||
group._screenResize();
|
||||
});
|
||||
}
|
||||
|
||||
public static _getResPool(): WindowResPool {
|
||||
return this._resPool;
|
||||
}
|
||||
}
|
85
src/ui/WindowResPool.ts
Normal file
85
src/ui/WindowResPool.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-13
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
import { UIObjectFactory } from "fairygui-cc";
|
||||
|
||||
export interface WindowInfo {
|
||||
/** 类的构造函数 */
|
||||
ctor: any;
|
||||
/** 窗口组名 */
|
||||
group: string;
|
||||
/** fgui包名 */
|
||||
pkg: string;
|
||||
/** 窗口名 */
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface HeaderInfo {
|
||||
ctor: any;
|
||||
pkg: string;
|
||||
}
|
||||
|
||||
export class WindowResPool {
|
||||
/** 窗口信息池 */
|
||||
protected _windowInfos: Map<string, WindowInfo> = new Map<string, any>();
|
||||
/** 窗口header信息池 */
|
||||
protected _headerInfos: Map<string, HeaderInfo> = new Map<string, any>();
|
||||
|
||||
/** 可扩展 窗口资源引用计数 */
|
||||
|
||||
/**
|
||||
* 注册窗口信息
|
||||
* @param info
|
||||
*/
|
||||
public add(ctor: any, group: string, pkg: string, name: string): void {
|
||||
if (this.has(name)) {
|
||||
throw new Error(`窗口【${name}】信息已注册 请勿重复注册`);
|
||||
}
|
||||
this._windowInfos.set(name, {
|
||||
ctor: ctor,
|
||||
group: group,
|
||||
pkg: pkg,
|
||||
name: name
|
||||
});
|
||||
// 窗口组件扩展
|
||||
UIObjectFactory.setExtension(`ui://${pkg}/${name}`, ctor);
|
||||
}
|
||||
|
||||
public has(name: string): boolean {
|
||||
return this._windowInfos.has(name);
|
||||
}
|
||||
|
||||
public get(name: string): WindowInfo {
|
||||
if (!this.has(name)) {
|
||||
throw new Error(`窗口【${name}】未注册,请使用 _uidecorator.uiclass 注册窗口`);
|
||||
}
|
||||
return this._windowInfos.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册窗口header信息
|
||||
* @param info
|
||||
*/
|
||||
public addHeader(ctor: any, pkg: string, name: string): void {
|
||||
this._headerInfos.set(name, {
|
||||
ctor: ctor,
|
||||
pkg: pkg
|
||||
});
|
||||
// 窗口header扩展
|
||||
UIObjectFactory.setExtension(`ui://${pkg}/${name}`, ctor);
|
||||
}
|
||||
|
||||
public hasHeader(name: string): boolean {
|
||||
return this._headerInfos.has(name);
|
||||
}
|
||||
|
||||
public getHeader(name: string): HeaderInfo {
|
||||
if (!this.hasHeader(name)) {
|
||||
throw new Error(`窗口header【${name}】未注册,请使用 _uidecorator.uiheader 注册窗口header`);
|
||||
}
|
||||
return this._headerInfos.get(name);
|
||||
}
|
||||
}
|
30
src/ui/header.ts
Normal file
30
src/ui/header.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-08
|
||||
* @Description: 窗口的一些类型配置
|
||||
*/
|
||||
|
||||
/** 窗口显示时,对其他窗口的隐藏处理类型 */
|
||||
export enum WindowType {
|
||||
/** 不做任何处理 */
|
||||
Normal = 0,
|
||||
/** 关闭所有 */
|
||||
CloseAll = 1 << 0,
|
||||
/** 关闭上一个 */
|
||||
CloseOne = 1 << 1,
|
||||
/** 隐藏所有 */
|
||||
HideAll = 1 << 2,
|
||||
/** 隐藏上一个 */
|
||||
HideOne = 1 << 3,
|
||||
}
|
||||
|
||||
/** 窗口适配类型,默认全屏 */
|
||||
export enum AdapterType {
|
||||
/** 全屏适配 */
|
||||
Full = 0,
|
||||
/** 空出刘海 */
|
||||
Bang = 1,
|
||||
/** 固定的 不适配 */
|
||||
Fixed = 2,
|
||||
}
|
||||
|
Reference in New Issue
Block a user