Commit 7db18c3c authored by rockyl's avatar rockyl

自动布局

parent fc82b76e
This diff is collapsed.
This diff is collapsed.
...@@ -5433,6 +5433,11 @@ declare module engine { ...@@ -5433,6 +5433,11 @@ declare module engine {
export function injectProperties(target: any, source: any): any; export function injectProperties(target: any, source: any): any;
export function transPoint(str: any, sep?: string): {
x: number;
y: number;
}
export function md5(string: any): string; export function md5(string: any): string;
export class ObjectPool { export class ObjectPool {
...@@ -5568,7 +5573,7 @@ declare module engine { ...@@ -5568,7 +5573,7 @@ declare module engine {
PROCESS = "process" PROCESS = "process"
} }
export function getLogSwitch(id: any): boolean; export function getLogSwitch(id: any): boolean | Array;
export class Process { export class Process {
private readonly id; private readonly id;
...@@ -5826,6 +5831,10 @@ declare module engine { ...@@ -5826,6 +5831,10 @@ declare module engine {
*/ */
hitTestPoint(globalPoint: Point, isMouseEvent?: boolean): any; hitTestPoint(globalPoint: Point, isMouseEvent?: boolean): any;
hitTestSelf(globalPoint: any): this;
hitTestSelfBounds(globalPoint: any): this;
/** /**
* webgl渲染 * webgl渲染
* @param {WebglRenderer} renderer - The renderer * @param {WebglRenderer} renderer - The renderer
...@@ -6361,6 +6370,8 @@ declare module engine { ...@@ -6361,6 +6370,8 @@ declare module engine {
export function getTexture(str: string): any; export function getTexture(str: string): any;
export let queryParams: any;
export function httpRequest(url: string, method?: string, params?: any, type?: 'text' | 'json' | 'jsonp', headers?: any): any; export function httpRequest(url: string, method?: string, params?: any, type?: 'text' | 'json' | 'jsonp', headers?: any): any;
export function jsonp(url: any, params: any): any; export function jsonp(url: any, params: any): any;
...@@ -6369,6 +6380,30 @@ declare module engine { ...@@ -6369,6 +6380,30 @@ declare module engine {
export function createTextureSheet(baseTexture: BaseTexture, altaData: any): void; export function createTextureSheet(baseTexture: BaseTexture, altaData: any): void;
export function parseSheet(sheet: any): void;
export function parseAssetConfig(assetConfig: any, uuid?: any): void;
export function parse(assetConfig: any, data: any): void;
export function getFont(name: any): any;
export function loadAssets(config: any, onProgress?: any, onComplete?: any): any;
export function getAssetByUUID(uuid: any): any;
export function getAssetByName(name: any): any;
export function playSound(uuid: any, options?: any, name?: any): any;
export function stopSound(name: any): void;
export function destroySound(name: any): void;
export function mute(muted?: boolean): void;
export function preloadSound(url: any, uuid: any): void;
export class Loader extends EventDispatcher { export class Loader extends EventDispatcher {
/** /**
* 记录原始数据,json和image,贴图在建立时会被缓存 * 记录原始数据,json和image,贴图在建立时会被缓存
...@@ -6393,6 +6428,8 @@ declare module engine { ...@@ -6393,6 +6428,8 @@ declare module engine {
loadText(url: string, uuid?: string): any; loadText(url: string, uuid?: string): any;
loadSound(url: string, uuid?: string): any;
loadImage(url: string, uuid?: string): any; loadImage(url: string, uuid?: string): any;
/** /**
...@@ -8098,20 +8135,6 @@ declare module engine { ...@@ -8098,20 +8135,6 @@ declare module engine {
popAll(view?: DisplayObject, options?: any): void; popAll(view?: DisplayObject, options?: any): void;
} }
export function parseSheet(sheet: any): void;
export function parseAssetConfig(assetConfig: any, uuid?: any): void;
export function parse(assetConfig: any, data: any): void;
export function getFont(name: any): any;
export function loadAssets(config: any, onProgress?: any, onComplete?: any): any;
export function getAssetByUUID(uuid: any): any;
export function getAssetByName(name: any): any;
export class ShapeBase extends Graphics { export class ShapeBase extends Graphics {
protected __fieldDirty: boolean; protected __fieldDirty: boolean;
fillColor: any; fillColor: any;
...@@ -8200,9 +8223,10 @@ declare module engine { ...@@ -8200,9 +8223,10 @@ declare module engine {
placeholderColor: any; placeholderColor: any;
maxLength: number; maxLength: number;
type: string; type: string;
pattern: string; charRegStr: string;
private _oldFillColor; private _oldFillColor;
private _oldStrokeColor; private _oldStrokeColor;
private _charReg;
constructor(); constructor();
...@@ -8212,13 +8236,16 @@ declare module engine { ...@@ -8212,13 +8236,16 @@ declare module engine {
protected _setText(value: any): void; protected _setText(value: any): void;
private setMaxLength; private updateMaxLength;
private updateWithCharReg;
private setCharReg;
private showPlaceholderLabel; private showPlaceholderLabel;
setFocus(): void; setFocus(): void;
setBlur(): void; setBlur(): void;
private onFocus;
private onBlur; private onBlur;
private onInput; private onInput;
private onClickStage; private onClickStage;
...@@ -8275,6 +8302,9 @@ declare module engine { ...@@ -8275,6 +8302,9 @@ declare module engine {
} }
export class HtmlView extends FloatDisplay { export class HtmlView extends FloatDisplay {
constructor();
afterConstructor(): void;
} }
export function registerNodeType(name: any, def: any): void; export function registerNodeType(name: any, def: any): void;
...@@ -8374,12 +8404,6 @@ declare module engine { ...@@ -8374,12 +8404,6 @@ declare module engine {
export function getProps(id: any): any; export function getProps(id: any): any;
export function playSound(uuid: any, options?: any, name?: any): any;
export function stopSound(name: any): void;
export function destroySound(name: any): void;
export const _default: { export const _default: {
onProgress(done: any, total: any): void; onProgress(done: any, total: any): void;
onComplete(): void; onComplete(): void;
...@@ -8389,13 +8413,11 @@ declare module engine { ...@@ -8389,13 +8413,11 @@ declare module engine {
export function launch(url: any, loadingDelegate?: any, onStart?: any): any; export function launch(url: any, loadingDelegate?: any, onStart?: any): any;
export function launchWithLocalStorage(id: any, loadingDelegate?: any, onStart?: any): any; export function launchWithLocalStorage(id: any, loadingDelegate?: any, onStart?: any): Promise<any>;
export function launchWithWindowVariable(name: any, loadingDelegate?: any, onStart?: any): any;
export function launchWithConfig(config: any, loadingDelegate?: any, onStart?: any): any; export function launchWithWindowVariable(name: any, loadingDelegate?: any, onStart?: any): Promise<any>;
export let queryParams: any; export function launchWithConfig(config: any, loadingDelegate?: any, onStart?: any): Promise<any>;
export const emojiRegexp: RegExp; export const emojiRegexp: RegExp;
......
import {DisplayObject} from './DisplayObject'; import {DisplayObject} from './DisplayObject';
import {Rectangle} from "../math/Rectangle"; import {Rectangle} from "../math/Rectangle";
import {Point} from '../math'; import {Point} from '../math/index';
import CanvasRenderer from '../renderers/CanvasRenderer'; import CanvasRenderer from '../renderers/CanvasRenderer';
import {Event} from "../events/Event" import {Event} from "../events/Event"
import {WebglRenderer} from '../renderers/WebglRenderer'; import {WebglRenderer} from '../renderers/WebglRenderer';
...@@ -9,7 +9,8 @@ import {applyAutoAdjust} from "../../zeroing/decorators/auto-adjust"; ...@@ -9,7 +9,8 @@ import {applyAutoAdjust} from "../../zeroing/decorators/auto-adjust";
import {applyScript} from "../../zeroing/decorators/scripts"; import {applyScript} from "../../zeroing/decorators/scripts";
import {applyEvents} from "../../zeroing/decorators/events"; import {applyEvents} from "../../zeroing/decorators/events";
import {afterConstructor} from "../../zeroing/decorators/after-constructor"; import {afterConstructor} from "../../zeroing/decorators/after-constructor";
import {injectProperties, instantiateScript} from "../../zeroing/utils"; import {injectProperties, instantiateScript} from "../../zeroing/utils/index";
import {isUI} from "../../zeroing/game-warpper/nodes/IUIComponent";
/** /**
* 容器类 * 容器类
...@@ -30,6 +31,8 @@ export default class Container extends DisplayObject { ...@@ -30,6 +31,8 @@ export default class Container extends DisplayObject {
horizonCenter: number; horizonCenter: number;
verticalCenter: number; verticalCenter: number;
private _lastLocalID;
/** /**
* 为false鼠标事件不再向下传递 * 为false鼠标事件不再向下传递
*/ */
...@@ -407,6 +410,12 @@ export default class Container extends DisplayObject { ...@@ -407,6 +410,12 @@ export default class Container extends DisplayObject {
child.updateTransform(); child.updateTransform();
} }
} }
if(isUI(this) && this._lastLocalID !== this.transform.localID){
this._lastLocalID = this.transform.localID;
//console.log(this.name, this.instanceId , 'dirty!');
this.stage.layoutInvalid = true;
}
} }
/** /**
......
import Container from "./Container"; import Container from "./Container";
import {devicePixelRatio, osType, RENDERER_TYPE, StageScaleMode} from "../const" import {devicePixelRatio, osType, RENDERER_TYPE, StageScaleMode} from "../const"
import SystemRenderer from "../renderers/SystemRenderer"; import SystemRenderer from "../renderers/SystemRenderer";
import {Point, Rectangle} from "../math"; import {Point, Rectangle} from "../math/index";
import {EventDispatcher} from "../events/EventDispatcher"; import {EventDispatcher} from "../events/EventDispatcher";
import {Event} from "../events/Event"; import {Event} from "../events/Event";
import {FloatDisplay} from "./FloatDisplay"; import {FloatDisplay} from "./FloatDisplay";
...@@ -11,7 +11,7 @@ import {MouseEvent} from "../events/MouseEvent"; ...@@ -11,7 +11,7 @@ import {MouseEvent} from "../events/MouseEvent";
import {WebglRenderer} from "../renderers/WebglRenderer"; import {WebglRenderer} from "../renderers/WebglRenderer";
import {GDispatcher} from "../events/GDispatcher"; import {GDispatcher} from "../events/GDispatcher";
import CanvasRenderer from "../renderers/CanvasRenderer"; import CanvasRenderer from "../renderers/CanvasRenderer";
import {GlobalPro, isWebGLSupported} from "../utils"; import {GlobalPro, isWebGLSupported} from "../utils/index";
//如果以后还出现帧率问题,使用ticker; //如果以后还出现帧率问题,使用ticker;
//兼容requestAnimationFrame //兼容requestAnimationFrame
...@@ -115,6 +115,11 @@ export class Stage extends Container { ...@@ -115,6 +115,11 @@ export class Stage extends Container {
*/ */
private static _stageList: any = {}; private static _stageList: any = {};
/**
* 布局失效
*/
layoutInvalid: boolean = false;
/** /**
* 是否暂停 * 是否暂停
* @property pause * @property pause
...@@ -132,7 +137,7 @@ export class Stage extends Container { ...@@ -132,7 +137,7 @@ export class Stage extends Container {
this._pause = value; this._pause = value;
if (value != this._pause) { if (value != this._pause) {
//触发事件 //触发事件
GDispatcher.dispatchEvent("onStagePause", { pause: value }); GDispatcher.dispatchEvent("onStagePause", {pause: value});
} }
} }
...@@ -575,7 +580,7 @@ export class Stage extends Container { ...@@ -575,7 +580,7 @@ export class Stage extends Container {
vH *= ih / 100; vH *= ih / 100;
} }
} }
return { w: vW, h: vH }; return {w: vW, h: vH};
} }
/** /**
...@@ -1005,6 +1010,14 @@ export class Stage extends Container { ...@@ -1005,6 +1010,14 @@ export class Stage extends Container {
} }
} }
afterUpdateTransform() {
this.calculateBounds();
if (this.layoutInvalid) {
this.dispatchEvent(Event.LAYOUT_INVALID);
this.layoutInvalid = false;
}
}
public destroy(): void { public destroy(): void {
let s = this; let s = this;
Stage.removeUpdateObj(s); Stage.removeUpdateObj(s);
......
...@@ -231,6 +231,15 @@ export class Event extends HashObject { ...@@ -231,6 +231,15 @@ export class Event extends HashObject {
* @type {string} * @type {string}
*/ */
public static TIMER_COMPLETE: string = "onTimerComplete"; public static TIMER_COMPLETE: string = "onTimerComplete";
/**
* 布局失效事件
* @property LAYOUT_INVALID
* @since 1.0.0
* @static
* @public
* @type {string}
*/
public static LAYOUT_INVALID: string = "LAYOUT_INVALID";
/** /**
* 数据更新事件 * 数据更新事件
* @property UPDATE_DATA * @property UPDATE_DATA
......
...@@ -101,6 +101,13 @@ export default class Transform extends HashObject { ...@@ -101,6 +101,13 @@ export default class Transform extends HashObject {
this._localID++; this._localID++;
} }
/**
* 返回local矩阵的id
*/
get localID() {
return this._localID;
}
/** /**
* 当斜切改变时,先记录,优化计算 * 当斜切改变时,先记录,优化计算
* @private * @private
......
...@@ -131,11 +131,11 @@ export default class CanvasRenderer extends SystemRenderer { ...@@ -131,11 +131,11 @@ export default class CanvasRenderer extends SystemRenderer {
} }
displayObject.parent = this._tempDisplayObjectParent; displayObject.parent = this._tempDisplayObjectParent;
displayObject.updateTransform(); displayObject.updateTransform();
displayObject.parent = cacheParent; displayObject.parent = cacheParent;
displayObject.stage && displayObject.stage.afterUpdateTransform();
//初始化上下文状态 //初始化上下文状态
context.save(); context.save();
context.setTransform(1, 0, 0, 1, 0, 0); context.setTransform(1, 0, 0, 1, 0, 0);
......
...@@ -179,6 +179,8 @@ export class WebglRenderer extends SystemRenderer { ...@@ -179,6 +179,8 @@ export class WebglRenderer extends SystemRenderer {
displayObject.updateTransform(); displayObject.updateTransform();
displayObject.parent = cacheParent; displayObject.parent = cacheParent;
displayObject.stage && displayObject.stage.afterUpdateTransform();
//绑定渲染对象,没有则是默认root //绑定渲染对象,没有则是默认root
this.bindRenderTexture(renderTexture, transform); this.bindRenderTexture(renderTexture, transform);
......
...@@ -4,4 +4,4 @@ ...@@ -4,4 +4,4 @@
export {ScrollViewBase} from "./ScrollViewBase"; export {ScrollViewBase} from "./ScrollViewBase";
export {ScrollListBase} from "./ScrollListBase"; export {ScrollListBase} from "./ScrollListBase";
export {Button} from "./Button"; //export {Button} from "./Button";
\ No newline at end of file \ No newline at end of file
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
* 自适应功能 * 自适应功能
*/ */
import {Event} from "../../2d/events"; import {Event} from "../../2d/events/index";
import {Stage} from "../../2d/display";
/** /**
* 应用自适应 * 应用自适应
...@@ -93,16 +92,10 @@ class AdjustProxy { ...@@ -93,16 +92,10 @@ class AdjustProxy {
} }
adjustLayout() { adjustLayout() {
return;
const that = this._host; const that = this._host;
let pWidth, pHeight; const {width: pWidth, height: pHeight} = that.parent;
//if(that.parent instanceof Stage){
// pWidth = that.parent.desWidth;
// pHeight = that.parent.desHeight;
//}else{
pWidth = that.parent.width;
pHeight = that.parent.height;
//}
const {width, height} = that; const {width, height} = that;
const {percentWidth, percentHeight, left, top, right, bottom, horizonCenter, verticalCenter} = this.data; const {percentWidth, percentHeight, left, top, right, bottom, horizonCenter, verticalCenter} = this.data;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Created by rockyl on 2019-11-05. * Created by rockyl on 2019-11-05.
*/ */
import {Container, Stage} from "../../2d/display/index"; import {Stage} from "../../2d/display/index";
import {StackContainer} from "./StackContainer"; import {StackContainer} from "./StackContainer";
import {loadAssets} from "./assets-manager"; import {loadAssets} from "./assets-manager";
import {instantiate} from "./view-interpreter"; import {instantiate} from "./view-interpreter";
...@@ -14,11 +14,12 @@ import {injectEnv} from "./enviroment"; ...@@ -14,11 +14,12 @@ import {injectEnv} from "./enviroment";
import {Toast} from "./Toast"; import {Toast} from "./Toast";
import {arrayFind} from "../utils"; import {arrayFind} from "../utils";
import {registerCustomModules, registerScripts} from ".."; import {registerCustomModules, registerScripts} from "..";
import {Node} from "./nodes/Node";
/** /**
* 游戏舞台 * 游戏舞台
*/ */
export class GameStage extends Container { export class GameStage extends Node {
private _sceneContainer: StackContainer; //场景容器 private _sceneContainer: StackContainer; //场景容器
private _popupContainer: StackContainer; //弹层容器 private _popupContainer: StackContainer; //弹层容器
private _toast: Toast; private _toast: Toast;
......
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
* Created by rockyl on 2019-11-05. * Created by rockyl on 2019-11-05.
*/ */
import {Container, DisplayObject} from "../../2d/display/index"; import {DisplayObject} from "../../2d/display/index";
import {Node} from "./nodes/Node";
/** /**
* 栈式视图容器 * 栈式视图容器
*/ */
export class StackContainer extends Container { export class StackContainer extends Node {
private _mutex: boolean; private _mutex: boolean;
private _stack = []; private _stack = [];
......
...@@ -2,14 +2,13 @@ ...@@ -2,14 +2,13 @@
* Created by rockyl on 2019-11-25. * Created by rockyl on 2019-11-25.
*/ */
import {Container} from "../../2d/display/index";
import {GameStage} from "./GameStage"; import {GameStage} from "./GameStage";
import {Label, Rect} from "./nodes"; import {Label, Rect, Node} from "./nodes/index";
import {Tween} from "../../2d/tween"; import {Tween} from "../../2d/tween/index";
export class Toast extends Container { export class Toast extends Node {
private _contentSample: Container; private _contentSample: Node;
private _content: Container; private _content: Node;
private _gameStage: GameStage; private _gameStage: GameStage;
...@@ -80,7 +79,7 @@ export class Toast extends Container { ...@@ -80,7 +79,7 @@ export class Toast extends Container {
} }
if (!view) { if (!view) {
if (!this._contentSample) { if (!this._contentSample) {
this._contentSample = new Container(); this._contentSample = new Node();
let bg = new Rect(); let bg = new Rect();
bg.borderRadius = 10; bg.borderRadius = 10;
bg.percentWidth = 100; bg.percentWidth = 100;
......
/**
* Created by rockyl on 2020-03-03.
*/
import {Stage} from "../../2d/display/Stage";
import {Event} from "../../2d/events/Event";
import {traverseViewNode} from "../utils/utils";
import {isUI} from "./nodes/IUIComponent";
let _stage: Stage;
export function initAutoLayout(stage: Stage) {
_stage = stage;
_stage.addEventListener(Event.LAYOUT_INVALID, onLayoutInvalid)
}
function onLayoutInvalid() {
console.log('onLayoutInvalid');
//traverseViewNode(_stage, adjustLayout);
}
function adjustLayout(node) {
if (!isUI(node)) {
return;
}
const {width, height, parent: {width: pWidth, height: pHeight},} = node;
const {percentWidth, percentHeight, left, top, right, bottom, horizonCenter, verticalCenter} = node;
const applyPercentWidth = function () {
if (t(percentWidth)) {
node.width = pWidth * percentWidth / 100;
}
};
const applyPercentHeight = function () {
if (t(percentHeight)) {
node.height = pHeight * percentHeight / 100;
}
};
let pw = true, ph = true;
if (t(horizonCenter)) {
applyPercentWidth();
node.x = (pWidth - node.width) / 2 + horizonCenter;
} else {
if (t(left)) {
node.x = left;
if (t(right)) {
node.width = pWidth - left - right;
pw = false;
}
} else if (t(right)) {
node.x = pWidth - width - right;
}
if (pw) {
applyPercentWidth();
}
}
if (t(verticalCenter)) {
applyPercentHeight();
node.y = (pHeight - node.height) / 2 + verticalCenter;
} else {
if (t(top)) {
node.y = top;
if (t(bottom)) {
node.height = pHeight - top - bottom;
ph = false;
}
} else if (t(bottom)) {
node.y = pHeight - height - bottom;
}
if (ph) {
applyPercentHeight();
}
}
}
function t(v) {
return !isNaN(v) && v !== null && v !== undefined;
}
/**
* Created by Raykid on 2016/12/16.
*/
import {IAres, Compiler, AresOptions, IWatcher, WatcherCallback, AresCommandData} from "./Interfaces";
import {Mutator} from "./Mutator";
import {Watcher} from "./Watcher";
import {CommandContext, Command, commands} from "./Commands"
export const defaultCmdRegExp:RegExp = /^(data\-)?a[\-_](\w+)([:\$](.+))?$/;
/**
* 将数据模型和视图进行绑定
* @param data
* @param compiler 视图解析器,不同类型的视图需要使用不同的解析器解析后方可使用
* @param options 一些额外参数
* @returns {IAres} 绑定实体对象
*/
export function bind(data:any, compiler:Compiler, options?:AresOptions):IAres
{
return new Ares(data, compiler, options);
}
export class Ares implements IAres
{
private _data:any;
private _compiler:Compiler;
private _options:any;
/** 获取ViewModel */
public get data():any
{
return this._data;
}
/** 获取编译器 */
public get compiler():Compiler
{
return this._compiler;
}
public constructor(data:any, compiler:Compiler, options?:AresOptions)
{
// 记录变异对象
this._data = Mutator.mutate(data);
this._compiler = compiler;
this._options = options;
// 初始化Compiler
this._compiler.init(this);
// 调用回调
if(this._options && this._options.inited)
{
this._options.inited.call(this._data, this);
}
}
public createWatcher(target:any, exp:string, scope:any, callback:WatcherCallback):IWatcher
{
return new Watcher(this, target, exp, scope, callback);
}
/**
* 解析表达式成为命令数据
* @param key 属性名,合法的属性名应以a-或a_开头,以:或$分隔主命令和子命令
* @param value 属性值,如果属性名合法则会被用来作为表达式的字符串
* @param cmdRegExp 可选,如果不传则使用默认的命令正则表达式解析命令
* @return {CommandData|null} 命令数据,如果不是命令则返回null
*/
public parseCommand(key:string, value:string, cmdRegExp?:RegExp):AresCommandData
{
var result:RegExpExecArray = (cmdRegExp || defaultCmdRegExp).exec(key);
if(!result) return null;
// 取到key
var key:string = result[0];
// 取到命令名
var cmdName:string = result[2];
// 取到命令字符串
var exp:string = value;
// 取到子命令名
var subCmd:string = result[4] || "";
// 返回结构体
return {
cmdName: cmdName,
subCmd: subCmd,
propName: key,
exp: exp
};
}
/**
* 测试是否是通用命令
* @param data 命令数据
* @return {boolean} 返回一个布尔值,表示该表达式是否是通用命令
*/
public testCommand(data:AresCommandData):boolean
{
// 非空判断
if(!data) return false;
// 取到通用命令
var cmd:Command = commands[data.cmdName];
return (cmd != null);
}
/**
* 执行通用命令,如果该表达式是通用命令则直接执行,否则什么都不做
* @param data 命令数据
* @param target 目标对象
* @param scope 变量作用域
* @return {boolean} 返回一个布尔值,表示该表达式是否是通用命令
*/
public execCommand(data:AresCommandData, target:any, scope:any):boolean
{
// 非空判断
if(!data || !scope) return false;
// 取到通用命令
var cmd:Command = commands[data.cmdName];
// 没找到命令就返回false
if(!cmd) return false;
// 找到命令了,执行之
cmd({
target: target,
scope: scope,
entity: this,
data: data
});
return true;
}
}
\ No newline at end of file
/**
* Created by Raykid on 2017/7/19.
*/
import {IAres, AresCommandData} from "./Interfaces"
import {runExp} from "./Utils"
export interface CommandContext
{
target:any;
scope:any;
entity:IAres;
data:AresCommandData;
}
export interface Command
{
/**
* 执行命令
* @param context 命令上下文
* @return {any} 要替换原显示节点的显示节点
*/
(context?:CommandContext):any;
}
export const commands:{[name:string]:Command} = {
/** 一次性设置变量命令,在数据中插入一个变量 */
set: (context:CommandContext)=>
{
// 设置变量值
runExp(context.data.subCmd + "=" + context.data.exp, context.scope);
return context.target;
},
/** 绑定设置变量命令,在数据中插入一个变量(如果不提供子命令则不插入变量),并根据表达式的值同步更新变量的值 */
bind: (context:CommandContext)=>
{
// 创建订阅器,监听表达式值变化
context.entity.createWatcher(context.target, context.data.exp, context.scope, (value:any)=>{
// 如果子命令不为空,则更新变量值
if(context.data.subCmd)
runExp(context.data.subCmd + "=" + context.data.exp, context.scope);
});
return context.target;
}
};
\ No newline at end of file
/**
* Created by Raykid on 2016/12/22.
*/
import {Watcher} from "./Watcher";
export class Dep
{
private _map:{[uid:number]:Watcher} = {};
/**
* 添加数据变更订阅者
* @param watcher 数据变更订阅者
*/
public watch(watcher:Watcher):void
{
if(!this._map[watcher.uid])
{
this._map[watcher.uid] = watcher;
}
}
/**
* 数据变更,通知所有订阅者
* @param extra 可能的额外数据
*/
public notify(extra?:any):void
{
for(var uid in this._map)
{
var watcher:Watcher = this._map[uid];
watcher.update(extra);
}
}
}
\ No newline at end of file
/**
* Created by Raykid on 2016/12/22.
*/
export interface Compiler
{
/** 传递给编译器的皮肤对象 */
root:any;
/**
* 初始化编译器
* @param entity Ares实例
*/
init(entity:IAres):void;
/**
* 编译方法
* @param target 要编译的显示节点
* @param scope 作用域
*/
compile(target:any, scope:any):void;
}
export interface IAres
{
/** 获取ViewModel */
data:any;
/** 获取编译器 */
compiler:Compiler;
/**
* 创建一个订阅者
* @param target 作用目标,指表达式所在的显示对象
* @param exp 表达式
* @param scope 作用域
* @param callback 订阅器回调
*/
createWatcher(target:any, exp:string, scope:any, callback:WatcherCallback):IWatcher;
/**
* 解析表达式成为命令数据
* @param key 属性名,合法的属性名应以a-或a_开头,以:或$分隔主命令和子命令
* @param value 属性值,如果属性名合法则会被用来作为表达式的字符串
* @return {CommandData|null} 命令数据,如果不是命令则返回null
*/
parseCommand(key:string, value:string):AresCommandData;
/**
* 测试是否是通用命令
* @param data 命令数据
* @return {boolean} 返回一个布尔值,表示该表达式是否是通用命令
*/
testCommand(data:AresCommandData):boolean;
/**
* 执行通用命令,如果该表达式是通用命令则直接执行,否则什么都不做
* @param data 命令数据
* @param target 目标对象
* @param scope 变量作用域
* @return {boolean} 返回一个布尔值,表示该表达式是否是通用命令
*/
execCommand(data:AresCommandData, target:any, scope:any):boolean
}
export interface AresOptions
{
inited?:(entity?:IAres)=>void;
}
export interface IWatcher
{
/**
* 获取到表达式当前最新值
* @returns {any} 最新值
*/
getValue():any;
/**
* 当依赖的数据有更新时调用该方法
* @param extra 可能的额外数据
*/
update(extra?:any):void;
/** 销毁订阅者 */
dispose():void;
}
export interface WatcherCallback
{
(newValue?:any, oldValue?:any, extra?:any):void;
}
export interface AresCommandData
{
/** 主命令名 */
cmdName:string;
/** 子命令名 */
subCmd:string;
/** 命令属性全名 */
propName:string;
/** 表达式 */
exp:string;
}
\ No newline at end of file
/**
* Created by Raykid on 2016/12/22.
*/
import {Watcher} from "./Watcher";
import {Dep} from "./Dep";
export class Mutator
{
// 记录数组中会造成数据更新的所有方法名
private static _arrMethods:string[] = [
"push",
"pop",
"unshift",
"shift",
"splice",
"sort",
"reverse"
];
/**
* 将用户传进来的数据“变异”成为具有截获数据变更能力的数据
* @param data 原始数据
* @returns {any} 变异后的数据
*/
public static mutate(data:any):any
{
// 如果是简单类型,则啥也不做
if(!data || typeof data != "object") return;
// 是个复杂类型对象,但是以前变异过了就不再重做一遍了
if(!data.__ares_mutated__)
{
// 针对每个内部变量都进行一次变异
for(var key in data)
{
Mutator.mutateObject(data, key, data[key]);
}
// 打一个标记表示已经变异过了
Object.defineProperty(data, "__ares_mutated__", {
value: true,
writable: false,
enumerable: false,
configurable: false
});
}
return data;
}
private static mutateObject(data:any, key:string, value:any):void
{
// 对每个复杂类型对象都要有一个对应的依赖列表
var dep:Dep = new Dep();
// 变异过程
Object.defineProperty(data, key, {
enumerable: true,
configurable: false,
get: ()=>{
// 如果Watcher.updating不是null,说明当前正在执行表达式,那么获取的变量自然是其需要依赖的
var watcher:Watcher = Watcher.updating;
if(watcher) dep.watch(watcher);
// 利用闭包保存原始值
return value;
},
set: v=>{
if(v == value) return;
value = v;
// 如果是数组就走专门的数组变异方法,否则递归变异对象
if(Array.isArray(v)) Mutator.mutateArray(v, dep);
else Mutator.mutate(v);
// 触发通知
dep.notify();
}
});
// 递归变异
Mutator.mutate(value);
}
private static mutateArray(arr:any[], dep:Dep):void
{
// 变异当前数组
arr["__proto__"] = Mutator.defineReactiveArray(dep);
// 遍历当前数组,将内容对象全部变异
for(var i:number = 0, len:number = arr.length; i < len; i++)
{
Mutator.mutate(arr[i]);
}
}
private static defineReactiveArray(dep:Dep):any[]
{
var proto:any[] = Array.prototype;
var result:any[] = Object.create(proto);
// 遍历所有方法,一个一个地变异
Mutator._arrMethods.forEach((method:string)=>{
// 利用闭包记录一个原始方法
var oriMethod:Function = proto[method];
// 开始变异
Object.defineProperty(result, method, {
value: function(...args:any[]):any
{
// 首先调用原始方法,获取返回值
var result:any = oriMethod.apply(this, args);
// 数组插入项
var inserted:any[];
switch(method)
{
case "push":
case "unshift":
inserted = args;
break
case "splice":
inserted = args.slice(2);
break
}
// 监视数组插入项,而不是重新监视整个数组
if(inserted && inserted.length)
{
Mutator.mutateArray(inserted, dep);
}
// 触发更新
dep.notify({method: args});
// 返回值
return result;
}
});
});
// 提供替换数组设置的方法,因为直接设置数组下标的方式无法变异
Object.defineProperty(result, "$set", {
value: function(index:number, value:any):any
{
// 超出数组长度默认追加到最后
if(index >= this.length) index = this.length;
return this.splice(index, 1, value)[0];
}
});
// 提供替换数组移除的方法,因为直接移除的方式无法变异
Object.defineProperty(result, "$remove", {
value: function(item:any):any
{
var index = this.indexOf(item);
if(index > -1) return this.splice(index, 1);
return null;
}
});
return result;
}
}
\ No newline at end of file
/**
* Created by Raykid on 2016/12/22.
*/
/**
* 创建一个表达式求值方法,用于未来执行
* @param exp 表达式
* @returns {Function} 创建的方法
*/
export function createEvalFunc(exp:string):(scope:any)=>any
{
var func:(scope:any)=>any;
try
{
func = Function("scope", "with(scope){return " + exp + "}") as (scope:any)=>any;
}
catch(err)
{
// 可能是某些版本的解释器不认识模板字符串,将模板字符串变成普通字符串
var sepStr:string = (exp.indexOf('"') < 0 ? '"' : "'");
// 将exp中的·替换为'
var reg:RegExp = /([^\\]?)`/g;
exp = exp.replace(reg, "$1" + sepStr);
// 将exp中${...}替换为" + ... + "的形式
reg = /\$\{(.*?)\}/g;
exp = exp.replace(reg, sepStr + "+($1)+" + sepStr);
// 重新生成方法并返回
func = Function("scope", "with(scope){return " + exp + "}") as (scope:any)=>any;
}
return func;
}
/**
* 表达式求值,无法执行多条语句
* @param exp 表达式
* @param scope 表达式的作用域
* @returns {any} 返回值
*/
export function evalExp(exp:string, scope:any):any
{
return createEvalFunc(exp)(scope);
}
/**
* 创建一个执行方法,用于未来执行
* @param exp 表达式
* @returns {Function} 创建的方法
*/
export function createRunFunc(exp:string):(scope:any)=>void
{
return createEvalFunc("(function(){" + exp + "})()");
}
/**
* 直接执行表达式,不求值。该方法可以执行多条语句
* @param exp 表达式
* @param scope 表达式的作用域
*/
export function runExp(exp:string, scope:any):void
{
createRunFunc(exp)(scope);
}
\ No newline at end of file
import {IAres, IWatcher, WatcherCallback} from "./Interfaces";
import {createEvalFunc} from "./Utils";
/**
* Created by Raykid on 2016/12/22.
* 数据更新订阅者,当依赖的数据有更新时会触发callback通知外面
*/
export class Watcher implements IWatcher
{
/** 记录当前正在执行update方法的Watcher引用 */
public static updating:Watcher = null;
private static _uid:number = 0;
private _uid:number;
/** 获取Watcher的全局唯一ID */
public get uid():number
{
return this._uid;
}
private _value:any;
private _entity:IAres;
private _target:any;
private _exp:string;
private _scope:any;
private _expFunc:(scope:any)=>any;
private _callback:WatcherCallback;
private _disposed:boolean = false;
public constructor(entity:IAres, target:any, exp:string, scope:any, callback:WatcherCallback)
{
// 记录entity
this._entity = entity;
// 生成一个全局唯一的ID
this._uid = Watcher._uid ++;
// 记录作用目标、表达式和作用域
this._target = target;
this._exp = exp;
this._scope = scope;
// 将表达式和作用域解析为一个Function
this._expFunc = createEvalFunc(exp);
// 记录回调函数
this._callback = callback;
// 进行首次更新
this.update();
}
/**
* 获取到表达式当前最新值
* @returns {any} 最新值
*/
public getValue():any
{
if(this._disposed) return null;
var value:any = null;
// 记录自身
Watcher.updating = this;
// 设置通用属性
// 这里一定要用defineProperty将目标定义在当前节点上,否则会影响context.scope
Object.defineProperty(this._scope, "$root", {
configurable: true,
enumerable: false,
value: this._entity.compiler.root,
writable: false
});
// 这里一定要用defineProperty将目标定义在当前节点上,否则会影响context.scope
Object.defineProperty(this._scope, "$target", {
configurable: true,
enumerable: false,
value: this._target,
writable: false
});
// 表达式求值
try
{
value = this._expFunc.call(this._scope, this._scope);
}
catch(err)
{
// 输出错误日志
console.warn("表达式求值错误\nerr: " + err.toString() + "\nexp:" + this._exp + ",scope:" + JSON.stringify(this._scope));
}
// 移除通用属性
delete this._scope["$root"];
delete this._scope["$target"];
// 移除自身记录
Watcher.updating = null;
return value;
}
/**
* 当依赖的数据有更新时调用该方法
* @param extra 可能的额外数据
*/
public update(extra?:any):void
{
if(this._disposed) return;
var value:any = this.getValue();
if(!Watcher.isEqual(value, this._value))
{
this._callback && this._callback(value, this._value, extra);
this._value = Watcher.deepCopy(value);
}
}
/** 销毁订阅者 */
public dispose():void
{
if(this._disposed) return;
this._value = null;
this._target = null;
this._exp = null;
this._scope = null;
this._expFunc = null;
this._callback = null;
this._disposed = true;
}
/**
* 是否相等,包括基础类型和对象/数组的对比
*/
private static isEqual(a:any, b:any):boolean
{
return (a == b || (
Watcher.isObject(a) && Watcher.isObject(b)
? JSON.stringify(a) == JSON.stringify(b)
: false
));
}
/**
* 是否为对象(包括数组、正则等)
*/
private static isObject(obj:any):boolean
{
return (obj && typeof obj == "object");
}
/**
* 复制对象,若为对象则深度复制
*/
private static deepCopy(from:any):any
{
if (Watcher.isObject(from))
{
// 复杂类型对象,先字符串化,再对象化
return JSON.parse(JSON.stringify(from));
}
else
{
// 基本类型对象,直接返回之
return from;
}
}
}
\ No newline at end of file
/**
* Created by rockyl on 2020-03-02.
*/
...@@ -8,8 +8,11 @@ import Texture from "../../../2d/texture/Texture"; ...@@ -8,8 +8,11 @@ import Texture from "../../../2d/texture/Texture";
import {getFont} from "../bmp-text-manager"; import {getFont} from "../bmp-text-manager";
import {dirtyFieldTrigger} from "../../decorators"; import {dirtyFieldTrigger} from "../../decorators";
import {VERTICAL_ALIGN} from "../../../2d/const"; import {VERTICAL_ALIGN} from "../../../2d/const";
import {IUIComponent} from "./IUIComponent";
export class BitmapText extends Container implements IUIComponent {
isUI = true;
export class BitmapText extends Container {
private _charCache = []; private _charCache = [];
private _fontConfig; private _fontConfig;
......
...@@ -2,17 +2,19 @@ ...@@ -2,17 +2,19 @@
* Created by rockyl on 2020-01-07. * Created by rockyl on 2020-01-07.
*/ */
import {FloatDisplay} from "../../../2d/display/index"; import {Container, FloatDisplay} from "../../../2d/display/index";
import {afterConstructor} from "../../decorators/after-constructor"; import {afterConstructor} from "../../decorators/after-constructor";
import {applyAutoAdjust} from "../../decorators/auto-adjust"; import {applyAutoAdjust} from "../../decorators/auto-adjust";
import {applyScript} from "../../decorators/scripts"; import {applyScript} from "../../decorators/scripts";
import {applyEvents} from "../../decorators/events"; import {applyEvents} from "../../decorators/events";
import {IUIComponent} from "./IUIComponent";
@afterConstructor @afterConstructor
@applyAutoAdjust @applyAutoAdjust
@applyScript @applyScript
@applyEvents @applyEvents
export class HtmlView extends FloatDisplay{ export class HtmlView extends FloatDisplay implements IUIComponent{
isUI = true;
constructor() { constructor() {
super(); super();
......
/**
* Created by rockyl on 2020-03-03.
*/
export interface IUIComponent {
isUI: boolean,
}
export function isUI(obj){
return obj.isUI;
}
/** /**
* Created by rockyl on 2019-11-07. * Created by rockyl on 2019-11-07.
*/ */
import {Sprite} from "../../../2d/display"; import {Sprite} from "../../../2d/display/index";
import Texture from "../../../2d/texture/Texture"; import Texture from "../../../2d/texture/Texture";
import {getAssetByName, getAssetByUUID} from "../assets-manager"; import {getAssetByName, getAssetByUUID} from "../assets-manager";
import {dataCenter} from "../data-center"; import {dataCenter} from "../data-center";
import {ESCAPE_REG_EXP} from "../../utils"; import {ESCAPE_REG_EXP} from "../../utils/index";
import {Event} from "../../../2d/events"; import {Event} from "../../../2d/events/index";
import {Container} from "../../../2d/display/index";
import {IUIComponent} from "./IUIComponent";
const assetScheme = 'asset://'; const assetScheme = 'asset://';
export class Image extends Sprite { export class Image extends Sprite implements IUIComponent {
isUI = true;
private _originText; private _originText;
private _escapes = []; private _escapes = [];
private _registeredEvents = []; private _registeredEvents = [];
......
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
import {TextField} from "../../../2d/text/index"; import {TextField} from "../../../2d/text/index";
import {dataCenter} from "../data-center"; import {dataCenter} from "../data-center";
import {ESCAPE_REG_EXP, htmlToPureText} from "../../utils/index"; import {ESCAPE_REG_EXP, htmlToPureText} from "../../utils/index";
import {Container} from "../../../2d/display/index";
import {IUIComponent} from "./IUIComponent";
/** /**
* 文本 * 文本
*/ */
export class Label extends TextField { export class Label extends TextField implements IUIComponent {
isUI = true;
private _originText; private _originText;
private _escapes = []; private _escapes = [];
private _registeredEvents = []; private _registeredEvents = [];
......
/**
* Created by rockyl on 2020-03-03.
*/
import {Container} from "../../../2d/display/index";
import {IUIComponent} from "./IUIComponent";
export class Node extends Container implements IUIComponent{
isUI = true;
}
\ No newline at end of file
...@@ -6,8 +6,11 @@ import {ScrollListBase, ScrollListItemBase} from "../../../2d/ui/ScrollListBase" ...@@ -6,8 +6,11 @@ import {ScrollListBase, ScrollListItemBase} from "../../../2d/ui/ScrollListBase"
import {Event} from "../../../2d/events"; import {Event} from "../../../2d/events";
import {proxyMethods} from "./ScrollView"; import {proxyMethods} from "./ScrollView";
import Container from "../../../2d/display/Container"; import Container from "../../../2d/display/Container";
import {IUIComponent} from "./IUIComponent";
export class ScrollListItem extends ScrollListItemBase implements IUIComponent {
isUI = true;
export class ScrollListItem extends ScrollListItemBase {
view: Container; view: Container;
setView(view) { setView(view) {
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
*/ */
import {ScrollViewBase} from "../../../2d/ui/ScrollViewBase"; import {ScrollViewBase} from "../../../2d/ui/ScrollViewBase";
import {Container} from "../../../2d/display/index";
import {IUIComponent} from "./IUIComponent";
export const proxyMethods = [ export const proxyMethods = [
'onChildrenChange', 'onChildrenChange',
...@@ -18,7 +20,9 @@ export const proxyMethods = [ ...@@ -18,7 +20,9 @@ export const proxyMethods = [
'removeChildren', 'removeChildren',
]; ];
export class ScrollView extends ScrollViewBase { export class ScrollView extends ScrollViewBase implements IUIComponent {
isUI = true;
constructor() { constructor() {
super(); super();
......
...@@ -9,6 +9,8 @@ import {TextField} from "../../../2d/text/index"; ...@@ -9,6 +9,8 @@ import {TextField} from "../../../2d/text/index";
import {Point} from "../../../2d/math/index"; import {Point} from "../../../2d/math/index";
import {dirtyFieldTrigger} from "../../decorators"; import {dirtyFieldTrigger} from "../../decorators";
import {VERTICAL_ALIGN} from "../../../2d/const"; import {VERTICAL_ALIGN} from "../../../2d/const";
import {Container} from "../../../2d/display/index";
import {IUIComponent} from "./IUIComponent";
let timer; let timer;
...@@ -26,7 +28,9 @@ function cancelDelayScrollTop() { ...@@ -26,7 +28,9 @@ function cancelDelayScrollTop() {
} }
} }
export class TextInput extends Label { export class TextInput extends Label implements IUIComponent {
isUI = true;
private _floatDisplay: FloatDisplay; private _floatDisplay: FloatDisplay;
private _placeholderLabel: TextField; private _placeholderLabel: TextField;
private _input: any; private _input: any;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Created by rockyl on 2019-11-07. * Created by rockyl on 2019-11-07.
*/ */
export * from './Node'
export * from './shapes' export * from './shapes'
export * from './Image' export * from './Image'
export * from './Label' export * from './Label'
...@@ -10,3 +11,5 @@ export * from './ScrollView' ...@@ -10,3 +11,5 @@ export * from './ScrollView'
export * from './ScrollList' export * from './ScrollList'
export * from './BitmapText' export * from './BitmapText'
export * from './HtmlView' export * from './HtmlView'
export * from './IUIComponent'
...@@ -5,11 +5,15 @@ import Color from "color"; ...@@ -5,11 +5,15 @@ import Color from "color";
import {Event} from "../../../2d/events"; import {Event} from "../../../2d/events";
import {dirtyFieldDetector} from "../../decorators"; import {dirtyFieldDetector} from "../../decorators";
import Graphics from "../../../2d/graphics/Graphics"; import Graphics from "../../../2d/graphics/Graphics";
import {Container} from "../../../2d/display/index";
import {IUIComponent} from "./IUIComponent";
/** /**
* 图形基类 * 图形基类
*/ */
class ShapeBase extends Graphics { class ShapeBase extends Graphics implements IUIComponent {
isUI = true;
protected __fieldDirty = true; protected __fieldDirty = true;
@dirtyFieldDetector @dirtyFieldDetector
......
...@@ -2,12 +2,11 @@ ...@@ -2,12 +2,11 @@
* Created by rockyl on 2019-11-08. * Created by rockyl on 2019-11-08.
*/ */
import {Container} from "../../2d/display/index"; import {Node, Rect, Image, Label, Circle, ScrollView, TextInput, ScrollList, BitmapText, HtmlView} from "./nodes/index";
import {Rect, Image, Label, Circle, ScrollView, TextInput, ScrollList, BitmapText, HtmlView} from "./nodes/index";
import {injectProperties, instantiateScript,} from "../utils/index"; import {injectProperties, instantiateScript,} from "../utils/index";
const nodeTypeMapping = { const nodeTypeMapping = {
node: Container, node: Node,
rect: Rect, rect: Rect,
circle: Circle, circle: Circle,
label: Label, label: Label,
......
...@@ -10,6 +10,7 @@ import {globalLoader} from "../2d/loader/Loader"; ...@@ -10,6 +10,7 @@ import {globalLoader} from "../2d/loader/Loader";
import {Event} from "../2d/events/Event"; import {Event} from "../2d/events/Event";
import builtinLoadingView from "./game-warpper/LoadingView"; import builtinLoadingView from "./game-warpper/LoadingView";
import {queryParams} from "./web"; import {queryParams} from "./web";
import {initAutoLayout} from "./game-warpper/auto-layout";
export let gameStage: GameStage; export let gameStage: GameStage;
...@@ -59,6 +60,7 @@ export async function launchWithConfig(config, loadingDelegate?, onStart?) { ...@@ -59,6 +60,7 @@ export async function launchWithConfig(config, loadingDelegate?, onStart?) {
scaleMode || StageScaleMode.FIXED_WIDTH, scaleMode || StageScaleMode.FIXED_WIDTH,
rendererType || RENDERER_TYPE.WEBGL, rendererType || RENDERER_TYPE.WEBGL,
); );
initAutoLayout(stage);
Stage.flushAll(); Stage.flushAll();
stage.addEventListener(Event.ON_INIT_STAGE, () => { stage.addEventListener(Event.ON_INIT_STAGE, () => {
......
/** /**
* Created by rockyl on 2019-11-08. * Created by rockyl on 2019-11-08.
*/ */
import Container from "../../2d/display/Container";
export const ESCAPE_REG_EXP = /\$\{[\u4e00-\u9fa5_a-zA-Z0-9\|]+\}/g; export const ESCAPE_REG_EXP = /\$\{[\u4e00-\u9fa5_a-zA-Z0-9\|]+\}/g;
export const linkedFlag = '$_linked_$'; export const linkedFlag = '$_linked_$';
...@@ -314,3 +315,18 @@ export function injectProperties(target, source) { ...@@ -314,3 +315,18 @@ export function injectProperties(target, source) {
return target; return target;
} }
/**
* 遍历视图节点
* @param node
* @param callback
*/
export function traverseViewNode(node: Container, callback: Function) {
for (let child of node.children) {
callback(child);
if (child.children && child.children.length > 0) {
traverseViewNode(child, callback);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment