You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdLike2048/assets/mx/module/gm/GmBuiltinPanels.ts

263 lines
9.0 KiB

1 day ago
import { Button, Color, EditBox, Label, Node, Overflow, UITransform, HorizontalTextAlignment, VerticalTextAlignment } from 'cc';
import { GmBuiltinPanelId, type IGmConfirmData, type IGmContentPanel, type IGmNumberStepperData, type IGmPanelContext, type IGmTextAreaData } from './GmTypes';
import { GmRegistry } from './GmRegistry';
/**
* 在父节点下创建带 Label 的按钮。
*/
function makeBtn(parent: Node, title: string, w: number, h: number, onClick: () => void): Node {
const n = new Node(title);
n.layer = parent.layer;
const ut = n.addComponent(UITransform);
ut.setContentSize(w, h);
const lb = n.addComponent(Label);
lb.string = title;
lb.fontSize = 22;
lb.horizontalAlign = HorizontalTextAlignment.CENTER;
lb.verticalAlign = VerticalTextAlignment.CENTER;
lb.color = Color.WHITE;
n.addComponent(Button);
n.on(Button.EventType.CLICK, onClick, null);
parent.addChild(n);
return n;
}
/**
* 数字步进面板。
*/
class GmNumberStepperPanel implements IGmContentPanel {
private _root: Node | null = null;
private _value = 0;
private _mode: 'set' | 'add' = 'set';
private _lbValue: Label | null = null;
private _lbMode: Label | null = null;
private _ctx: IGmPanelContext | null = null;
private _data: IGmNumberStepperData | null = null;
open(parent: Node, ctx: IGmPanelContext): void {
this.close();
this._ctx = ctx;
this._data = ctx.command.panelData as IGmNumberStepperData;
if (!this._data?.getValue || !this._data?.apply) {
console.warn('[GM] numberStepper 缺少 panelData');
ctx.host.toast('面板配置错误');
ctx.goBack();
return;
}
this._value = Math.floor(Number(this._data.getValue()) || 0);
this._mode = this._data.defaultMode ?? 'set';
this._root = new Node('numberStepper');
this._root.layer = parent.layer;
const rootUt = this._root.addComponent(UITransform);
rootUt.setContentSize(640, 400);
parent.addChild(this._root);
const title = new Node('title');
title.layer = parent.layer;
title.addComponent(UITransform).setContentSize(600, 40);
const tl = title.addComponent(Label);
tl.string = ctx.command.title;
tl.fontSize = 28;
tl.horizontalAlign = HorizontalTextAlignment.CENTER;
tl.color = Color.WHITE;
title.setPosition(0, 160);
this._root.addChild(title);
const valNode = new Node('val');
valNode.layer = parent.layer;
valNode.addComponent(UITransform).setContentSize(200, 50);
this._lbValue = valNode.addComponent(Label);
this._lbValue.fontSize = 36;
this._lbValue.horizontalAlign = HorizontalTextAlignment.CENTER;
this._lbValue.color = Color.YELLOW;
valNode.setPosition(0, 80);
this._root.addChild(valNode);
const modeNode = new Node('mode');
modeNode.layer = parent.layer;
modeNode.addComponent(UITransform).setContentSize(200, 30);
this._lbMode = modeNode.addComponent(Label);
this._lbMode.fontSize = 22;
this._lbMode.horizontalAlign = HorizontalTextAlignment.CENTER;
this._lbMode.color = Color.CYAN;
modeNode.setPosition(0, 30);
this._root.addChild(modeNode);
const steps = [-100, -10, -1, 1, 10, 100];
for (let i = 0; i < steps.length; i++) {
const s = steps[i];
const bx = -250 + i * 100;
makeBtn(this._root, s > 0 ? `+${s}` : `${s}`, 88, 44, () => {
this._value += s;
this._clamp();
this._paint();
}).setPosition(bx, -30);
}
makeBtn(this._root, '切换模式', 140, 44, () => {
this._mode = this._mode === 'set' ? 'add' : 'set';
this._paint();
}).setPosition(-120, -100);
makeBtn(this._root, '应用', 140, 44, () => {
this._data!.apply(this._value, this._mode);
this._ctx!.refreshList();
this._ctx!.goBack();
}).setPosition(120, -100);
makeBtn(this._root, '返回', 100, 40, () => this._ctx!.goBack()).setPosition(0, -160);
this._paint();
}
close(): void {
if (this._root?.isValid) this._root.destroy();
this._root = null;
this._lbValue = null;
this._lbMode = null;
this._ctx = null;
this._data = null;
}
private _clamp(): void {
const min = this._data?.min;
const max = this._data?.max;
if (min !== undefined && this._value < min) this._value = min;
if (max !== undefined && this._value > max) this._value = max;
}
private _paint(): void {
if (this._lbValue) this._lbValue.string = `${this._value}`;
if (this._lbMode) this._lbMode.string = this._mode === 'set' ? '模式:设为' : '模式:增加';
}
}
/**
* 文本区面板(存档 JSON 等)。
*/
class GmTextAreaPanel implements IGmContentPanel {
private _root: Node | null = null;
private _edit: EditBox | null = null;
private _ctx: IGmPanelContext | null = null;
open(parent: Node, ctx: IGmPanelContext): void {
this.close();
this._ctx = ctx;
const data = ctx.command.panelData as IGmTextAreaData;
if (!data?.apply) {
console.warn('[GM] textArea 缺少 panelData');
ctx.host.toast('面板配置错误');
ctx.goBack();
return;
}
this._root = new Node('textArea');
this._root.layer = parent.layer;
this._root.addComponent(UITransform).setContentSize(640, 480);
parent.addChild(this._root);
const title = new Node('title');
title.layer = parent.layer;
title.addComponent(UITransform).setContentSize(600, 36);
const tl = title.addComponent(Label);
tl.string = ctx.command.title;
tl.fontSize = 26;
tl.horizontalAlign = HorizontalTextAlignment.CENTER;
tl.color = Color.WHITE;
title.setPosition(0, 200);
this._root.addChild(title);
const box = new Node('edit');
box.layer = parent.layer;
box.addComponent(UITransform).setContentSize(580, 280);
this._edit = box.addComponent(EditBox);
this._edit.placeholder = data.placeholder ?? '粘贴内容';
this._edit.string = '';
box.setPosition(0, 20);
this._root.addChild(box);
makeBtn(this._root, '应用', 140, 44, () => {
data.apply(this._edit?.string ?? '');
this._ctx!.refreshList();
this._ctx!.goBack();
}).setPosition(-100, -180);
makeBtn(this._root, '返回', 140, 44, () => this._ctx!.goBack()).setPosition(100, -180);
}
close(): void {
if (this._root?.isValid) this._root.destroy();
this._root = null;
this._edit = null;
this._ctx = null;
}
}
/**
* 二次确认面板。
*/
class GmConfirmPanel implements IGmContentPanel {
private _root: Node | null = null;
private _ctx: IGmPanelContext | null = null;
open(parent: Node, ctx: IGmPanelContext): void {
this.close();
this._ctx = ctx;
const data = ctx.command.panelData as IGmConfirmData;
if (!data?.onConfirm) {
console.warn('[GM] confirmQuick 缺少 panelData');
ctx.host.toast('面板配置错误');
ctx.goBack();
return;
}
this._root = new Node('confirm');
this._root.layer = parent.layer;
this._root.addComponent(UITransform).setContentSize(560, 280);
parent.addChild(this._root);
const msg = new Node('msg');
msg.layer = parent.layer;
msg.addComponent(UITransform).setContentSize(500, 120);
const lb = msg.addComponent(Label);
lb.string = data.message || ctx.command.title;
lb.fontSize = 24;
lb.horizontalAlign = HorizontalTextAlignment.CENTER;
lb.verticalAlign = VerticalTextAlignment.CENTER;
lb.overflow = Overflow.RESIZE_HEIGHT;
lb.color = Color.WHITE;
msg.setPosition(0, 40);
this._root.addChild(msg);
makeBtn(this._root, '确认', 140, 44, () => {
data.onConfirm();
this._ctx!.refreshList();
this._ctx!.goBack();
}).setPosition(-100, -80);
makeBtn(this._root, '取消', 140, 44, () => this._ctx!.goBack()).setPosition(100, -80);
}
close(): void {
if (this._root?.isValid) this._root.destroy();
this._root = null;
this._ctx = null;
}
}
/**
* 注册 mx 内置内容面板。
*/
export class GmBuiltinPanels {
private static _done = false;
/** 幂等注册内置面板 */
static ensureRegistered(): void {
if (this._done) return;
this._done = true;
GmRegistry.registerPanel(GmBuiltinPanelId.numberStepper, () => new GmNumberStepperPanel());
GmRegistry.registerPanel(GmBuiltinPanelId.textArea, () => new GmTextAreaPanel());
GmRegistry.registerPanel(GmBuiltinPanelId.confirmQuick, () => new GmConfirmPanel());
}
}