@senspark/ee
Version:
utility library for cocos creator
222 lines (189 loc) • 6.24 kB
text/typescript
import assert = require('assert');
import { Dialog, DialogEventType } from "./Dialog";
import { DialogManager } from './DialogManager';
enum CommandType {
/** Pushes a dialog. */
Push,
/** Pops a dialog. */
Pop,
}
class Command {
private type: CommandType;
private dialog: Dialog;
public constructor(type: CommandType, dialog: Dialog) {
this.type = type;
this.dialog = dialog;
}
public getType(): CommandType {
return this.type;
}
public getDialog(): Dialog {
return this.dialog;
}
}
export class DefaultDialogManager implements DialogManager {
private root: cc.Node;
private lockingDialog: Dialog | null;
private dialogStack: Dialog[];
private commandQueue: Command[];
public constructor(root: cc.Node) {
this.root = root;
this.lockingDialog = null;
this.dialogStack = [];
this.commandQueue = [];
}
public pushDialog(dialog: Dialog): void {
assert(dialog !== null);
assert(dialog.getContainer() !== null);
this.pushCommand(new Command(CommandType.Push, dialog));
this.processCommandQueue();
}
public popDialog(dialog: Dialog): void {
assert(dialog !== null);
assert(dialog.getContainer() !== null);
this.pushCommand(new Command(CommandType.Pop, dialog));
this.processCommandQueue();
}
/** Checks whether there is a locking dialog. */
private isLocked(): boolean {
return this.lockingDialog !== null;
}
/** Locks all dialog behaviors with the specified dialog. */
private lock(dialog: Dialog): void {
assert(this.lockingDialog === null);
this.lockingDialog = dialog;
}
/** Unlocks all dialog behaviors. */
private unlock(dialog: Dialog): void {
assert(this.lockingDialog === dialog);
this.lockingDialog = null;
}
/** Gets the current (top) dialog. */
private getCurrentDialog(): Dialog | null {
const length = this.dialogStack.length;
if (length === 0) {
return null;
}
const dialog = this.dialogStack[length - 1];
assert(dialog !== null);
return dialog;
}
/** Gets the current (top) root node. */
private getCurrentRoot(): cc.Node {
const dialog = this.getCurrentDialog();
if (dialog === null) {
return this.root;
}
return dialog.getContainer();
}
/** Attempts to process the current command queue. */
private processCommandQueue(): boolean {
if (this.isLocked()) {
return false;
}
const length = this.commandQueue.length;
for (let i = 0; i < length; ++i) {
const command = this.commandQueue[i];
if (this.processCommand(command)) {
this.commandQueue.splice(i, 1);
return true;
}
}
return false;
}
private processCommand(command: Command): boolean {
if (command.getType() === CommandType.Push) {
return this.processPushCommand(command.getDialog());
}
if (command.getType() === CommandType.Pop) {
return this.processPopCommand(command.getDialog());
}
assert(false);
return false;
}
private processPushCommand(dialog: Dialog): boolean {
this.pushDialogImmediately(dialog);
return true;
}
private processPopCommand(dialog: Dialog): boolean {
const topDialog = this.getCurrentDialog();
if (topDialog !== dialog) {
return false;
}
this.popDialogImmediately(dialog);
return true;
}
private pushCommand(command: Command): boolean {
const length = this.commandQueue.length;
if (length > 0) {
const lastCommand = this.commandQueue[length - 1];
if (lastCommand.getType() === command.getType() &&
lastCommand.getDialog() === command.getDialog()) {
// Duplicated command.
return false;
}
}
this.commandQueue.push(command);
return true;
}
/**
* Immediately pushes the specified dialog:
* - Lock dialog.
* - Will-hide events.
* - Add dialog's container.
* - Push dialog.
* - Did-hide events.
* - Set active = true.
* - Unlock dialog.
* - Process next commands.
*/
private pushDialogImmediately(dialog: Dialog): void {
assert(!dialog.isActive());
// Lock first.
this.lock(dialog);
// Must be called before push + addChild.
dialog.processEvent(DialogEventType.WillShow);
// Add child + push.
const root = this.getCurrentRoot();
root.addChild(dialog.getContainer());
this.dialogStack.push(dialog);
dialog.playShowingTransition(() => {
// Process event + set active.
dialog.processEvent(DialogEventType.DidShow);
dialog.setActive(true);
// Unlock and process next commands.
this.unlock(dialog);
this.processCommandQueue();
});
}
/**
* Immediately pops the specified dialog:
* - Lock dialog.
* - Set active = false.
* - Will-hide events.
* - Pop dialog.
* - Remove dialog's container.
* - Did-hide events.
* - Unlock dialog.
* - Process next commands.
*/
private popDialogImmediately(dialog: Dialog): void {
assert(dialog.isActive());
// Lock first.
this.lock(dialog);
// Set active + process event.
dialog.setActive(false);
dialog.processEvent(DialogEventType.WillHide);
dialog.playHidingTransition(() => {
// Pop + remove child.
this.dialogStack.pop();
const container = dialog.getContainer();
container.removeFromParent(false);
// Must be called after pop + remove child.
dialog.processEvent(DialogEventType.DidHide);
// Unlock and process next commands.
this.unlock(dialog);
this.processCommandQueue();
});
}
}