mirror of
https://github.com/Gongxh0901/kunpolibrary
synced 2025-04-20 02:18:41 +00:00
UI资源加载规则配置
This commit is contained in:
parent
35fe0474e7
commit
e2ab03b997
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kunpocc",
|
||||
"version": "1.0.19",
|
||||
"version": "1.0.20",
|
||||
"type": "module",
|
||||
"description": "基于creator3.0+的kunpocc库",
|
||||
"main": "./dist/kunpocc.cjs",
|
||||
|
@ -34,7 +34,7 @@ export class CocosUIModule extends ModuleBase {
|
||||
// this._uiInitializer.init(this.reAdaptWhenScreenResize, this.fullIfWideScreen);
|
||||
this.node.destroyAllChildren();
|
||||
/** 注册窗口信息 */
|
||||
WindowManager.registerUI()
|
||||
WindowManager.registerUI();
|
||||
this.onInit();
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ export abstract class WindowBase extends GComponent implements IWindow {
|
||||
return this._header as T;
|
||||
}
|
||||
|
||||
public setHeader<T extends IWindowHeader>(header: T): void {
|
||||
public _setHeader<T extends IWindowHeader>(header: T): void {
|
||||
this._header = header;
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,8 @@ export { Ticker as BTTicker } from "./behaviortree/Ticker";
|
||||
/** UI */
|
||||
export { Window } from "./fgui/Window";
|
||||
export { WindowHeader } from "./fgui/WindowHeader";
|
||||
export * from "./ui/header";
|
||||
export { AdapterType, WindowType } from "./ui/header";
|
||||
export { IPackageConfig } from "./ui/IPackageConfig";
|
||||
export { _uidecorator } from "./ui/UIDecorator";
|
||||
export { WindowGroup } from "./ui/WindowGroup";
|
||||
export { WindowHeaderInfo } from "./ui/WindowHeaderInfo";
|
||||
|
42
src/ui/IPackageConfig.ts
Normal file
42
src/ui/IPackageConfig.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2025-02-25
|
||||
* @Description: 包配置格式
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface IPackageConfig {
|
||||
/** UI所在resources中的路径 */
|
||||
uiPath: string;
|
||||
/**
|
||||
* 手动管理资源的包
|
||||
* 1. 用于基础UI包, 提供一些最基础的组件,所有其他包都可能引用其中的内容
|
||||
* 2. 资源header所在的包
|
||||
* 3. 用于一些特殊场景, 比如需要和其他资源一起加载, 并且显示进度条的包
|
||||
*/
|
||||
manualPackages: string[];
|
||||
/**
|
||||
* 不推荐配置 只是提供一种特殊需求的实现方式
|
||||
* 窗口引用到其他包中的资源 需要的配置信息
|
||||
*/
|
||||
linkPackages: { [windowName: string]: string[] };
|
||||
|
||||
/**
|
||||
* 关闭界面后,需要立即释放资源的包名(建议尽量少)
|
||||
* 一般不建议包进行频繁装载卸载,因为每次装载卸载必然是要消耗CPU时间(意味着耗电)和产生大量GC的。UI系统占用的内存是可以精确估算的,你可以按照包的使用频率设定哪些包是需要立即释放的。
|
||||
* 不包括手动管理的包
|
||||
*/
|
||||
imReleasePackages: string[];
|
||||
}
|
||||
|
||||
export interface IPackageConfigRes {
|
||||
/** 配置信息 */
|
||||
config: IPackageConfig;
|
||||
/** 显示加载等待窗 */
|
||||
showWaitWindow: () => void;
|
||||
/** 隐藏加载等待窗 */
|
||||
hideWaitWindow: () => void;
|
||||
/** 打开窗口时UI包加载失败 */
|
||||
fail: (windowName: string, errmsg: string, pkgs: string[]) => void;
|
||||
}
|
@ -75,5 +75,5 @@ export interface IWindow {
|
||||
/** 获取资源栏数据 */
|
||||
getHeaderInfo(): WindowHeaderInfo;
|
||||
|
||||
setHeader(header: IWindowHeader): void;
|
||||
_setHeader(header: IWindowHeader): void;
|
||||
}
|
@ -339,7 +339,7 @@ export class WindowGroup {
|
||||
let name = headerInfo.name;
|
||||
let header = this._getHeader(name);
|
||||
if (header) {
|
||||
window.setHeader(header);
|
||||
window._setHeader(header);
|
||||
header._addRef();
|
||||
} else {
|
||||
// 创建header节点
|
||||
@ -347,7 +347,7 @@ export class WindowGroup {
|
||||
let newHeader = UIPackage.createObject(pkg, name) as WindowHeader;
|
||||
newHeader.name = name;
|
||||
newHeader.opaque = false;
|
||||
window.setHeader(newHeader);
|
||||
window._setHeader(newHeader);
|
||||
newHeader.visible = false;
|
||||
PropsHelper.serializeProps(newHeader, pkg);
|
||||
newHeader._init();
|
||||
@ -390,4 +390,14 @@ export class WindowGroup {
|
||||
this._alphaGraph.setSize(Screen.ScreenWidth, Screen.ScreenHeight, true);
|
||||
this._alphaGraph.setPivot(0.5, 0.5, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭窗口组中的所有窗口
|
||||
*/
|
||||
public closeAllWindow(): void {
|
||||
while (this.size > 0) {
|
||||
let name = this.getTopWindowName();
|
||||
WindowManager.closeWindow(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
import { debug, warn } from "../tool/log";
|
||||
import { ComponentExtendHelper } from "./ComponentExtendHelper";
|
||||
import { IPackageConfigRes } from "./IPackageConfig";
|
||||
import { IWindow } from "./IWindow";
|
||||
import { _uidecorator } from "./UIDecorator";
|
||||
import { WindowGroup } from "./WindowGroup";
|
||||
@ -21,24 +22,39 @@ export class WindowManager {
|
||||
/** 初始化时传入实例 */
|
||||
private static _resPool: WindowResPool;
|
||||
|
||||
/**
|
||||
* 打开一个窗口
|
||||
* @param windowName 窗口名
|
||||
* @param userdata 用户数据
|
||||
*/
|
||||
public static showWindow(windowName: string, userdata?: any): void {
|
||||
//TODO::如果没有资源 加载资源
|
||||
this.showWindowIm(windowName, userdata);
|
||||
/** 配置UI包的一些信息 (可以不配置 完全手动管理) */
|
||||
public static initPackageConfig(res: IPackageConfigRes): void {
|
||||
this._resPool.initPackageConfig(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定名称的窗口,并传递可选的用户数据。
|
||||
* 异步打开一个窗口 (如果UI包的资源未加载, 会自动加载 配合 WindowManager.initPackageConfig一起使用)
|
||||
* @param windowName 窗口名
|
||||
* @param userdata 用户数据
|
||||
*/
|
||||
public static async showWindow(windowName: string, userdata?: any): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._resPool.loadWindowRes(windowName, {
|
||||
complete: () => {
|
||||
this.showWindowIm(windowName, userdata);
|
||||
resolve();
|
||||
},
|
||||
fail: (pkgs: string[]) => {
|
||||
reject(pkgs);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示指定名称的窗口,并传递可选的用户数据。(用于已加载过资源的窗口)
|
||||
* @param windowName - 窗口的名称。
|
||||
* @param userdata - 可选参数,用于传递给窗口的用户数据。
|
||||
*/
|
||||
public static showWindowIm(windowName: string, userdata?: any): void {
|
||||
const info = this._resPool.get(windowName);
|
||||
const windowGroup = this.getWindowGroup(info.group);
|
||||
this._resPool.addResRef(windowName);
|
||||
windowGroup.showWindow(info, userdata);
|
||||
}
|
||||
|
||||
@ -71,6 +87,25 @@ export class WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有窗口
|
||||
* @param ignoreNames 忽略关闭的窗口名
|
||||
*/
|
||||
public static closeAllWindow(ignoreNames: string[] = []): void {
|
||||
let existIgnore = ignoreNames.length > 0;
|
||||
this._windows.forEach((window: IWindow, name: string) => {
|
||||
if (!existIgnore) {
|
||||
this.closeWindow(name);
|
||||
} else if (!ignoreNames.includes(name)) {
|
||||
this.closeWindow(name);
|
||||
}
|
||||
});
|
||||
|
||||
if (!existIgnore) {
|
||||
this._windows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前最顶层的窗口实例。
|
||||
* @template T - 窗口实例的类型,必须继承自 IWindow 接口。
|
||||
@ -162,6 +197,7 @@ export class WindowManager {
|
||||
if (this.hasWindow(name)) {
|
||||
this._windows.get(name)._close();
|
||||
this._windows.delete(name);
|
||||
this._resPool.releaseWindowRes(name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,6 +211,7 @@ export class WindowManager {
|
||||
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()) {
|
||||
|
@ -4,7 +4,9 @@
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
import { UIObjectFactory } from "fairygui-cc";
|
||||
import { UIObjectFactory, UIPackage } from "fairygui-cc";
|
||||
import { warn } from "../kunpocc";
|
||||
import { IPackageConfigRes } from "./IPackageConfig";
|
||||
|
||||
export interface WindowInfo {
|
||||
/** 类的构造函数 */
|
||||
@ -28,7 +30,23 @@ export class WindowResPool {
|
||||
/** 窗口header信息池 */
|
||||
protected _headerInfos: Map<string, HeaderInfo> = new Map<string, any>();
|
||||
|
||||
/** 可扩展 窗口资源引用计数 */
|
||||
/** 是否设置过配置内容 */
|
||||
private _isInit: boolean = false;
|
||||
/** 窗口名对应的包名列表 */
|
||||
private _windowPkgs: Map<string, string[]> = new Map();
|
||||
/** 包的引用计数 */
|
||||
private _pkgRefs: { [pkg: string]: number } = {};
|
||||
private _uipath: string = "";
|
||||
private _manualPackages: Set<string> = new Set();
|
||||
private _imReleasePackages: Set<string> = new Set();
|
||||
|
||||
/** 注册的回调函数 */
|
||||
private _showWaitWindow: () => void = null;
|
||||
private _hideWaitWindow: () => void = null;
|
||||
private _fail: (windowName: string, errmsg: string, pkgs: string[]) => void = null;
|
||||
|
||||
/** 等待窗口的引用计数 */
|
||||
private _waitRef: number = 0;
|
||||
|
||||
/**
|
||||
* 注册窗口信息
|
||||
@ -44,6 +62,7 @@ export class WindowResPool {
|
||||
pkg: pkg,
|
||||
name: name
|
||||
});
|
||||
this.addWindowPkg(name, pkg);
|
||||
// 窗口组件扩展
|
||||
UIObjectFactory.setExtension(`ui://${pkg}/${name}`, ctor);
|
||||
}
|
||||
@ -82,4 +101,192 @@ export class WindowResPool {
|
||||
}
|
||||
return this._headerInfos.get(name);
|
||||
}
|
||||
|
||||
/** 资源配置相关接口 */
|
||||
public initPackageConfig(res: IPackageConfigRes): void {
|
||||
if (!res || !res.config) {
|
||||
return;
|
||||
}
|
||||
if (this._isInit) {
|
||||
throw new Error("资源配置已初始化,请勿重复设置");
|
||||
}
|
||||
this._isInit = true;
|
||||
this._showWaitWindow = res?.showWaitWindow;
|
||||
this._hideWaitWindow = res?.hideWaitWindow;
|
||||
this._fail = res?.fail;
|
||||
|
||||
this._uipath = res.config?.uiPath || "";
|
||||
// 如果uipath不以/结尾 则添加/
|
||||
if (this._uipath != "" && !this._uipath.endsWith("/")) {
|
||||
this._uipath += "/";
|
||||
}
|
||||
|
||||
this._manualPackages = new Set(res.config.manualPackages || []);
|
||||
this._imReleasePackages = new Set(res.config.imReleasePackages || []);
|
||||
|
||||
let windowPkgs = res.config.linkPackages || {};
|
||||
for (const windowName in windowPkgs) {
|
||||
let pkgs = windowPkgs[windowName];
|
||||
for (const pkg of pkgs || []) {
|
||||
this.addWindowPkg(windowName, pkg);
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历一遍,剔除手动管理的包
|
||||
this._windowPkgs.forEach((pkgs: string[], windowName: string) => {
|
||||
for (let i = pkgs.length - 1; i >= 0; i--) {
|
||||
if (this._manualPackages.has(pkgs[i])) {
|
||||
pkgs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
if (pkgs.length <= 0) {
|
||||
this._windowPkgs.delete(windowName);
|
||||
}
|
||||
});
|
||||
}
|
||||
/** 添加窗口对应的包名 */
|
||||
public addWindowPkg(windowName: string, pkgName: string): void {
|
||||
if (!this._windowPkgs.has(windowName)) {
|
||||
this._windowPkgs.set(windowName, [pkgName]);
|
||||
} else {
|
||||
this._windowPkgs.get(windowName).push(pkgName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载窗口需要的包资源
|
||||
* @param windowName 窗口名
|
||||
*/
|
||||
public loadWindowRes(windowName: string, listenter: { complete: () => void, fail: (pkgs: string[]) => void }): void {
|
||||
// 资源配置未初始化 直接返回成功
|
||||
if (!this._isInit) {
|
||||
warn(`UI包信息未配置 将手动管理所有UI包资源的加载,如果需要配置,请使用 【WindowManager.initPackageConfig】接口`);
|
||||
listenter.complete();
|
||||
return;
|
||||
}
|
||||
// 不需要包资源 直接返回成功
|
||||
if (!this.hasWindowPkg(windowName)) {
|
||||
listenter.complete();
|
||||
return;
|
||||
}
|
||||
if (this._waitRef++ <= 0) {
|
||||
// 调用注入的回调函数 用来显示等待窗
|
||||
this._showWaitWindow?.();
|
||||
}
|
||||
this.loadPackages({
|
||||
pkgs: this.getWindowPkgs(windowName),
|
||||
complete: () => {
|
||||
if (--this._waitRef <= 0) {
|
||||
// 调用注入的回调函数 关闭等待窗
|
||||
listenter.complete();
|
||||
this._hideWaitWindow?.();
|
||||
}
|
||||
},
|
||||
fail: (pkgs: string[]) => {
|
||||
warn(`界面${windowName}打开失败`);
|
||||
listenter.fail(pkgs);
|
||||
this._fail?.(windowName, "UI包加载失败", pkgs);
|
||||
if (--this._waitRef <= 0) {
|
||||
// 调用注入的回调函数 关闭等待窗
|
||||
this._hideWaitWindow?.();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public addResRef(windowName: string): void {
|
||||
if (!this._isInit) {
|
||||
return;
|
||||
}
|
||||
// 不需要包资源 直接返回成功
|
||||
if (!this.hasWindowPkg(windowName)) {
|
||||
return;
|
||||
}
|
||||
let pkgs = this.getWindowPkgs(windowName);
|
||||
for (const pkg of pkgs) {
|
||||
this.addRef(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放窗口资源
|
||||
* @param windowName 窗口名
|
||||
*/
|
||||
public releaseWindowRes(windowName: string): void {
|
||||
if (!this._isInit || !this.hasWindowPkg(windowName)) {
|
||||
return;
|
||||
}
|
||||
let pkgs = this.getWindowPkgs(windowName);
|
||||
for (const pkg of pkgs) {
|
||||
this.decRef(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载fgui包
|
||||
* @param pkgs 包名集合
|
||||
* @param progress 进度回调
|
||||
* @param complete 加载完成回调
|
||||
*/
|
||||
private loadPackages(res: { pkgs: string[], complete: () => void, fail: (pkgs: string[]) => void }): void {
|
||||
// 过滤已经加载的包
|
||||
let needLoadPkgs = res.pkgs.filter(pkg => this.getRef(pkg) <= 0);
|
||||
|
||||
let successPkgs: string[] = [];
|
||||
let failPkgs: string[] = [];
|
||||
let total: number = needLoadPkgs.length;
|
||||
if (total <= 0) {
|
||||
res.complete();
|
||||
return;
|
||||
}
|
||||
for (const pkg of needLoadPkgs) {
|
||||
UIPackage.loadPackage(this._uipath + pkg, (err: any) => {
|
||||
total--;
|
||||
err ? failPkgs.push(pkg) : successPkgs.push(pkg);
|
||||
if (total > 0) {
|
||||
return;
|
||||
}
|
||||
if (failPkgs.length > 0) {
|
||||
res.fail(failPkgs);
|
||||
} else {
|
||||
res.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取窗口对应的包名列表 */
|
||||
private getWindowPkgs(windowName: string): string[] {
|
||||
if (this._windowPkgs.has(windowName)) {
|
||||
return this._windowPkgs.get(windowName);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private hasWindowPkg(windowName: string): boolean {
|
||||
return this._windowPkgs.has(windowName);
|
||||
}
|
||||
|
||||
/** 获取包的引用计数 */
|
||||
private getRef(pkg: string): number {
|
||||
return this._pkgRefs[pkg] ? this._pkgRefs[pkg] : 0;
|
||||
}
|
||||
|
||||
/** 增加包的引用计数 */
|
||||
private addRef(pkg: string): void {
|
||||
this._pkgRefs[pkg] = this.getRef(pkg) + 1;
|
||||
}
|
||||
|
||||
/** 减少包的引用计数 */
|
||||
private decRef(pkg: string): void {
|
||||
this._pkgRefs[pkg] = this.getRef(pkg) - 1;
|
||||
if (this.getRef(pkg) <= 0) {
|
||||
delete this._pkgRefs[pkg];
|
||||
|
||||
// 如果需要立即释放 释放包资源
|
||||
if (this._imReleasePackages.has(pkg)) {
|
||||
UIPackage.removePackage(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user