babylon.smartPropertyPrim.ts 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. module BABYLON {
  2. export class Prim2DClassInfo {
  3. }
  4. export class Prim2DPropInfo {
  5. static PROPKIND_MODEL: number = 1;
  6. static PROPKIND_INSTANCE: number = 2;
  7. static PROPKIND_DYNAMIC: number = 3;
  8. id: number;
  9. flagId: number;
  10. kind: number;
  11. name: string;
  12. dirtyBoundingInfo: boolean;
  13. dirtyParentBoundingInfo: boolean;
  14. typeLevelCompare: boolean;
  15. bindingMode: number;
  16. bindingUpdateSourceTrigger: number;
  17. }
  18. export class ClassTreeInfo<TClass, TProp>{
  19. constructor(baseClass: ClassTreeInfo<TClass, TProp>, type: Object, classContentFactory: (base: TClass) => TClass) {
  20. this._baseClass = baseClass;
  21. this._type = type;
  22. this._subClasses = new Array<{ type: Object, node: ClassTreeInfo<TClass, TProp> }>();
  23. this._levelContent = new StringDictionary<TProp>();
  24. this._classContentFactory = classContentFactory;
  25. }
  26. get classContent(): TClass {
  27. if (!this._classContent) {
  28. this._classContent = this._classContentFactory(this._baseClass ? this._baseClass.classContent : null);
  29. }
  30. return this._classContent;
  31. }
  32. get type(): Object {
  33. return this._type;
  34. }
  35. get levelContent(): StringDictionary<TProp> {
  36. return this._levelContent;
  37. }
  38. get fullContent(): StringDictionary<TProp> {
  39. if (!this._fullContent) {
  40. let dic = new StringDictionary<TProp>();
  41. let curLevel: ClassTreeInfo<TClass, TProp> = this;
  42. while (curLevel) {
  43. curLevel.levelContent.forEach((k, v) => dic.add(k, v));
  44. curLevel = curLevel._baseClass;
  45. }
  46. this._fullContent = dic;
  47. }
  48. return this._fullContent;
  49. }
  50. getLevelOf(type: Object): ClassTreeInfo<TClass, TProp> {
  51. // Are we already there?
  52. if (type === this._type) {
  53. return this;
  54. }
  55. let baseProto = Object.getPrototypeOf(type);
  56. let curProtoContent = this.getOrAddType(Object.getPrototypeOf(baseProto), baseProto);
  57. if (!curProtoContent) {
  58. this.getLevelOf(baseProto);
  59. }
  60. return this.getOrAddType(baseProto, type);
  61. }
  62. getOrAddType(baseType: Object, type: Object): ClassTreeInfo<TClass, TProp> {
  63. // Are we at the level corresponding to the baseType?
  64. // If so, get or add the level we're looking for
  65. if (baseType === this._type) {
  66. for (let subType of this._subClasses) {
  67. if (subType.type === type) {
  68. return subType.node;
  69. }
  70. }
  71. let node = new ClassTreeInfo<TClass, TProp>(this, type, this._classContentFactory);
  72. let info = { type: type, node: node };
  73. this._subClasses.push(info);
  74. return info.node;
  75. }
  76. // Recurse down to keep looking for the node corresponding to the baseTypeName
  77. for (let subType of this._subClasses) {
  78. let info = subType.node.getOrAddType(baseType, type);
  79. if (info) {
  80. return info;
  81. }
  82. }
  83. return null;
  84. }
  85. static get<TClass, TProp>(type: Object): ClassTreeInfo<TClass, TProp> {
  86. let dic = <ClassTreeInfo<TClass, TProp>>type["__classTreeInfo"];
  87. if (!dic) {
  88. return null;
  89. }
  90. return dic.getLevelOf(type);
  91. }
  92. static getOrRegister<TClass, TProp>(type: Object, classContentFactory: (base: TClass) => TClass): ClassTreeInfo<TClass, TProp> {
  93. let dic = <ClassTreeInfo<TClass, TProp>>type["__classTreeInfo"];
  94. if (!dic) {
  95. dic = new ClassTreeInfo<TClass, TProp>(null, type, classContentFactory);
  96. type["__classTreeInfo"] = dic;
  97. }
  98. return dic;
  99. }
  100. private _type: Object;
  101. private _classContent: TClass;
  102. private _baseClass: ClassTreeInfo<TClass, TProp>;
  103. private _subClasses: Array<{ type: Object, node: ClassTreeInfo<TClass, TProp> }>;
  104. private _levelContent: StringDictionary<TProp>;
  105. private _fullContent: StringDictionary<TProp>;
  106. private _classContentFactory: (base: TClass) => TClass;
  107. }
  108. @className("DataBinding", "BABYLON")
  109. export class DataBinding {
  110. /**
  111. * Use the mode specified in the SmartProperty declaration
  112. */
  113. static MODE_DEFAULT: number = 1;
  114. /**
  115. * Update the binding target only once when the Smart Property's value is first accessed
  116. */
  117. static MODE_ONETIME: number = 2;
  118. /**
  119. * Update the smart property when the source changes.
  120. * The source won't be updated if the smart property value is set.
  121. */
  122. static MODE_ONEWAY: number = 3;
  123. /**
  124. * Only update the source when the target's data is changing.
  125. */
  126. static MODE_ONEWAYTOSOURCE: number = 4;
  127. /**
  128. * Update the bind target when the source changes and update the source when the Smart Property value is set.
  129. */
  130. static MODE_TWOWAY: number = 5;
  131. /**
  132. * Use the Update Source Trigger defined in the SmartProperty declaration
  133. */
  134. static UPDATESOURCETRIGGER_DEFAULT: number = 1;
  135. /**
  136. * Update the source as soon as the Smart Property has a value change
  137. */
  138. static UPDATESOURCETRIGGER_PROPERTYCHANGED: number = 2;
  139. /**
  140. * Update the source when the binding target loses focus
  141. */
  142. static UPDATESOURCETRIGGER_LOSTFOCUS: number = 3;
  143. /**
  144. * Update the source will be made by explicitly calling the UpdateFromDataSource method
  145. */
  146. static UPDATESOURCETRIGGER_EXPLICIT: number = 4;
  147. constructor() {
  148. this._converter = null;
  149. this._mode = DataBinding.MODE_DEFAULT;
  150. this._uiElementId = null;
  151. this._dataSource = null;
  152. this._currentDataSource = null;
  153. this._propertyPathName = null;
  154. this._stringFormat = null;
  155. this._updateSourceTrigger = DataBinding.UPDATESOURCETRIGGER_PROPERTYCHANGED;
  156. this._boundTo = null;
  157. this._owner = null;
  158. this._updateCounter = 0;
  159. }
  160. /**
  161. * Provide a callback that will convert the value obtained by the Data Binding to the type of the SmartProperty it's bound to.
  162. * If no value are set, then it's assumed that the sourceValue is of the same type as the SmartProperty's one.
  163. * If the SmartProperty type is a basic data type (string, boolean or number) and no converter is specified but the sourceValue is of a different type, the conversion will be implicitly made, if possible.
  164. * @param sourceValue the source object retrieve by the Data Binding mechanism
  165. * @returns the object of a compatible type with the SmartProperty it's bound to
  166. */
  167. public get converter(): (sourceValue: any) => any {
  168. return this._converter;
  169. }
  170. public set converter(value: (sourceValue: any) => any) {
  171. if (this._converter === value) {
  172. return;
  173. }
  174. this._converter = value;
  175. }
  176. /**
  177. * Set the mode to use for the data flow in the binding. Set one of the MODE_xxx static member of this class. If not specified then MODE_DEFAULT will be used
  178. */
  179. public get mode(): number {
  180. if (this._mode === DataBinding.MODE_DEFAULT) {
  181. return this._boundTo.bindingMode;
  182. }
  183. return this._mode;
  184. }
  185. public set mode(value: number) {
  186. if (this._mode === value) {
  187. return;
  188. }
  189. this._mode = value;
  190. }
  191. /**
  192. * You can override the Data Source object with this member which is the Id of a uiElement existing in the UI Logical tree.
  193. * If not set and source no set too, then the dataSource property will be used.
  194. */
  195. public get uiElementId(): string {
  196. return this._uiElementId;
  197. }
  198. public set uiElementId(value: string) {
  199. if (this._uiElementId === value) {
  200. return;
  201. }
  202. this._uiElementId = value;
  203. }
  204. /**
  205. * You can override the Data Source object with this member which is the source object to use directly.
  206. * If not set and uiElement no set too, then the dataSource property of the SmartPropertyBase object will be used.
  207. */
  208. public get dataSource(): IPropertyChanged {
  209. return this._dataSource;
  210. }
  211. public set dataSource(value: IPropertyChanged) {
  212. if (this._dataSource === value) {
  213. return;
  214. }
  215. this._dataSource = value;
  216. }
  217. /**
  218. * The path & name of the property to get from the source object.
  219. * Once the Source object is evaluated (it's either the one got from uiElementId, source or dataSource) you can specify which property of this object is the value to bind to the smartProperty.
  220. * If nothing is set then the source object will be used.
  221. * You can specify an indirect property using the format "firstProperty.indirectProperty" like "address.postalCode" if the source is a Customer object which contains an address property and the Address class contains a postalCode property.
  222. * If the property is an Array and you want to address a particular element then use the 'arrayProperty[index]' notation. For example "phoneNumbers[0]" to get the first element of the phoneNumber property which is an array.
  223. */
  224. public get propertyPathName(): string {
  225. return this._propertyPathName;
  226. }
  227. public set propertyPathName(value: string) {
  228. if (this._propertyPathName === value) {
  229. return;
  230. }
  231. if (this._owner) {
  232. //BindingWatcher.unregisterBinding(this, null);
  233. }
  234. this._propertyPathName = value;
  235. if (this._owner) {
  236. //let watched = BindingWatcher._getDataSource(this._owner.dataSource, this);
  237. //BindingWatcher.refreshBinding(watched, this._owner, this, true, null, true);
  238. }
  239. }
  240. /**
  241. * If the Smart Property is of the string type, you can use the string interpolation notation to provide how the sourceValue will be formatted, reference to the source value must be made via the token: ${value}. For instance `Customer Name: ${value}`
  242. */
  243. public get stringFormat(): (value: any) => string {
  244. return this._stringFormat;
  245. }
  246. public set stringFormat(value: (value: any) => string) {
  247. if (this._stringFormat === value) {
  248. return;
  249. }
  250. this._stringFormat = value;
  251. }
  252. /**
  253. * Specify how the source should be updated, use one of the UPDATESOURCETRIGGER_xxx member of this class, if not specified then UPDATESOURCETRIGGER_DEFAULT will be used.
  254. */
  255. public get updateSourceTrigger(): number {
  256. return this._updateSourceTrigger;
  257. }
  258. public set updateSourceTrigger(value: number) {
  259. if (this._updateSourceTrigger === value) {
  260. return;
  261. }
  262. this._updateSourceTrigger = value;
  263. }
  264. canUpdateTarget(resetUpdateCounter: boolean): boolean {
  265. if (resetUpdateCounter) {
  266. this._updateCounter = 0;
  267. }
  268. let mode = this.mode;
  269. if (mode === DataBinding.MODE_ONETIME) {
  270. return this._updateCounter === 0;
  271. }
  272. if (mode === DataBinding.MODE_ONEWAYTOSOURCE) {
  273. return false;
  274. }
  275. return true;
  276. }
  277. updateTarget() {
  278. let value = this._getActualDataSource();
  279. let properties = this.propertyPathName.split(".");
  280. for (let propertyName of properties) {
  281. value = value[propertyName];
  282. }
  283. this._storeBoundValue(this._owner, value);
  284. }
  285. public _storeBoundValue(watcher: SmartPropertyBase, value) {
  286. if ((++this._updateCounter > 1) && (this.mode === DataBinding.MODE_ONETIME)) {
  287. return;
  288. }
  289. let newValue = value;
  290. if (this._converter) {
  291. newValue = this._converter(value);
  292. }
  293. if (this._stringFormat) {
  294. newValue = this._stringFormat(newValue);
  295. }
  296. watcher[this._boundTo.name] = newValue;
  297. }
  298. private _getActualDataSource() {
  299. if (this.dataSource) {
  300. return this.dataSource;
  301. }
  302. if (this.uiElementId) {
  303. // TODO Find UIElement
  304. return null;
  305. }
  306. return this._owner.dataSource;
  307. }
  308. public _registerDataSource(updateTarget: boolean) {
  309. let ds = this._getActualDataSource();
  310. if (ds === this._currentDataSource) {
  311. return;
  312. }
  313. if (this._currentDataSource) {
  314. BindingHelper.unregisterDataSource(this._currentDataSource, this, 0);
  315. }
  316. if (ds) {
  317. BindingHelper.registerDataSource(ds, this);
  318. if (updateTarget && this.canUpdateTarget(true)) {
  319. this.updateTarget();
  320. }
  321. }
  322. this._currentDataSource = ds;
  323. }
  324. public _unregisterDataSource() {
  325. let ds = this._getActualDataSource();
  326. if (ds) {
  327. BindingHelper.unregisterDataSource(ds, this, 0);
  328. }
  329. }
  330. /**
  331. * The PropInfo of the property the binding is bound to
  332. */
  333. public _boundTo: Prim2DPropInfo;
  334. public _owner: SmartPropertyBase;
  335. private _converter: (sourceValue: any) => any;
  336. private _mode: number;
  337. private _uiElementId: string;
  338. private _dataSource: IPropertyChanged;
  339. public _currentDataSource: IPropertyChanged;
  340. private _propertyPathName: string;
  341. private _stringFormat: (value: any) => string;
  342. private _updateSourceTrigger: number;
  343. private _updateCounter: number;
  344. }
  345. @className("SmartPropertyBase", "BABYLON")
  346. export abstract class SmartPropertyBase extends PropertyChangedBase {
  347. constructor() {
  348. super();
  349. this._dataSource = null;
  350. this._dataSourceObserver = null;
  351. this._instanceDirtyFlags = 0;
  352. this._isDisposed = false;
  353. this._bindings = null;
  354. this._hasBinding = 0;
  355. this._bindingSourceChanged = 0;
  356. this._disposeObservable = null;
  357. }
  358. public get disposeObservable(): Observable<SmartPropertyBase> {
  359. if (!this._disposeObservable) {
  360. this._disposeObservable = new Observable<SmartPropertyBase>();
  361. }
  362. return this._disposeObservable;
  363. }
  364. /**
  365. * Check if the object is disposed or not.
  366. * @returns true if the object is dispose, false otherwise.
  367. */
  368. public get isDisposed(): boolean {
  369. return this._isDisposed;
  370. }
  371. /**
  372. * Disposable pattern, this method must be overloaded by derived types in order to clean up hardware related resources.
  373. * @returns false if the object is already dispose, true otherwise. Your implementation must call super.dispose() and check for a false return and return immediately if it's the case.
  374. */
  375. public dispose(): boolean {
  376. if (this.isDisposed) {
  377. return false;
  378. }
  379. if (this._disposeObservable && this._disposeObservable.hasObservers()) {
  380. this._disposeObservable.notifyObservers(this);
  381. }
  382. this._isDisposed = true;
  383. return true;
  384. }
  385. /**
  386. * Check if a given set of properties are dirty or not.
  387. * @param flags a ORed combination of Prim2DPropInfo.flagId values
  388. * @return true if at least one property is dirty, false if none of them are.
  389. */
  390. public checkPropertiesDirty(flags: number): boolean {
  391. return (this._instanceDirtyFlags & flags) !== 0;
  392. }
  393. /**
  394. * Clear a given set of properties.
  395. * @param flags a ORed combination of Prim2DPropInfo.flagId values
  396. * @return the new set of property still marked as dirty
  397. */
  398. protected clearPropertiesDirty(flags: number): number {
  399. this._instanceDirtyFlags &= ~flags;
  400. return this._instanceDirtyFlags;
  401. }
  402. public _resetPropertiesDirty() {
  403. this._instanceDirtyFlags = 0;
  404. }
  405. /**
  406. * Add an externally attached data from its key.
  407. * This method call will fail and return false, if such key already exists.
  408. * If you don't care and just want to get the data no matter what, use the more convenient getOrAddExternalDataWithFactory() method.
  409. * @param key the unique key that identifies the data
  410. * @param data the data object to associate to the key for this Engine instance
  411. * @return true if no such key were already present and the data was added successfully, false otherwise
  412. */
  413. public addExternalData<T>(key: string, data: T): boolean {
  414. if (!this._externalData) {
  415. this._externalData = new StringDictionary<Object>();
  416. }
  417. return this._externalData.add(key, data);
  418. }
  419. /**
  420. * Get an externally attached data from its key
  421. * @param key the unique key that identifies the data
  422. * @return the associated data, if present (can be null), or undefined if not present
  423. */
  424. public getExternalData<T>(key: string): T {
  425. if (!this._externalData) {
  426. return null;
  427. }
  428. return <T>this._externalData.get(key);
  429. }
  430. /**
  431. * Get an externally attached data from its key, create it using a factory if it's not already present
  432. * @param key the unique key that identifies the data
  433. * @param factory the factory that will be called to create the instance if and only if it doesn't exists
  434. * @return the associated data, can be null if the factory returned null.
  435. */
  436. public getOrAddExternalDataWithFactory<T>(key: string, factory: (k: string) => T): T {
  437. if (!this._externalData) {
  438. this._externalData = new StringDictionary<Object>();
  439. }
  440. return <T>this._externalData.getOrAddWithFactory(key, factory);
  441. }
  442. /**
  443. * Remove an externally attached data from the Engine instance
  444. * @param key the unique key that identifies the data
  445. * @return true if the data was successfully removed, false if it doesn't exist
  446. */
  447. public removeExternalData(key): boolean {
  448. if (!this._externalData) {
  449. return false;
  450. }
  451. return this._externalData.remove(key);
  452. }
  453. static _hookProperty<T>(propId: number, piStore: (pi: Prim2DPropInfo) => void, kind: number,
  454. settings?: {
  455. bindingMode?: number,
  456. bindingUpdateSourceTrigger?: number,
  457. typeLevelCompare?: boolean,
  458. dirtyBoundingInfo?: boolean,
  459. dirtyParentBoundingBox?: boolean,
  460. }): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {
  461. return (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => {
  462. if (!settings) {
  463. settings = {};
  464. }
  465. var propInfo = SmartPropertyBase._createPropInfo(target, <string>propName, propId, kind, settings);
  466. if (piStore) {
  467. piStore(propInfo);
  468. }
  469. let getter = descriptor.get, setter = descriptor.set;
  470. let typeLevelCompare = (settings.typeLevelCompare!==undefined) ? settings.typeLevelCompare : false;
  471. // Overload the property setter implementation to add our own logic
  472. descriptor.set = function (val) {
  473. if (!setter) {
  474. throw Error(`Property '${propInfo.name}' of type '${Tools.getFullClassName(this)}' has no setter defined but was invoked as if it had one.`);
  475. }
  476. // check for disposed first, do nothing
  477. if (this.isDisposed) {
  478. return;
  479. }
  480. let curVal = getter.call(this);
  481. if (SmartPropertyBase._checkUnchanged(curVal, val)) {
  482. return;
  483. }
  484. // Cast the object we're working one
  485. let prim = <SmartPropertyBase>this;
  486. // Change the value
  487. setter.call(this, val);
  488. // Notify change, dirty flags update
  489. prim._handlePropChanged(curVal, val, <string>propName, propInfo, typeLevelCompare);
  490. }
  491. }
  492. }
  493. private static _createPropInfo(target: Object, propName: string, propId: number, kind: number,
  494. settings: {
  495. bindingMode?: number,
  496. bindingUpdateSourceTrigger?: number,
  497. typeLevelCompare?: boolean,
  498. dirtyBoundingInfo?: boolean,
  499. dirtyParentBoundingBox?: boolean,
  500. }): Prim2DPropInfo {
  501. let dic = ClassTreeInfo.getOrRegister<Prim2DClassInfo, Prim2DPropInfo>(target, () => new Prim2DClassInfo());
  502. var node = dic.getLevelOf(target);
  503. let propInfo = node.levelContent.get(propId.toString());
  504. if (propInfo) {
  505. throw new Error(`The ID ${propId} is already taken by another property declaration named: ${propInfo.name}`);
  506. }
  507. // Create, setup and add the PropInfo object to our prop dictionary
  508. propInfo = new Prim2DPropInfo();
  509. propInfo.id = propId;
  510. propInfo.flagId = Math.pow(2, propId);
  511. propInfo.kind = kind;
  512. propInfo.name = propName;
  513. propInfo.bindingMode = (settings.bindingMode !== undefined) ? settings.bindingMode : DataBinding.MODE_TWOWAY;
  514. propInfo.bindingUpdateSourceTrigger = (settings.bindingUpdateSourceTrigger !== undefined) ? settings.bindingUpdateSourceTrigger : DataBinding.UPDATESOURCETRIGGER_PROPERTYCHANGED;
  515. propInfo.dirtyBoundingInfo = (settings.dirtyBoundingInfo!==undefined) ? settings.dirtyBoundingInfo : false;
  516. propInfo.dirtyParentBoundingInfo = (settings.dirtyParentBoundingBox!==undefined) ? settings.dirtyParentBoundingBox : false;
  517. propInfo.typeLevelCompare = (settings.typeLevelCompare!==undefined) ? settings.typeLevelCompare : false;
  518. node.levelContent.add(propName, propInfo);
  519. return propInfo;
  520. }
  521. /**
  522. * Access the dictionary of properties metadata. Only properties decorated with XXXXLevelProperty are concerned
  523. * @returns the dictionary, the key is the property name as declared in Javascript, the value is the metadata object
  524. */
  525. protected get propDic(): StringDictionary<Prim2DPropInfo> {
  526. if (!this._propInfo) {
  527. let cti = ClassTreeInfo.get<Prim2DClassInfo, Prim2DPropInfo>(Object.getPrototypeOf(this));
  528. if (!cti) {
  529. throw new Error("Can't access the propDic member in class definition, is this class SmartPropertyPrim based?");
  530. }
  531. this._propInfo = cti.fullContent;
  532. }
  533. return this._propInfo;
  534. }
  535. private static _checkUnchanged(curValue, newValue): boolean {
  536. // Nothing to nothing: nothing to do!
  537. if ((curValue === null && newValue === null) || (curValue === undefined && newValue === undefined)) {
  538. return true;
  539. }
  540. // Check value unchanged
  541. if ((curValue != null) && (newValue != null)) {
  542. if (typeof (curValue.equals) == "function") {
  543. if (curValue.equals(newValue)) {
  544. return true;
  545. }
  546. } else {
  547. if (curValue === newValue) {
  548. return true;
  549. }
  550. }
  551. }
  552. return false;
  553. }
  554. private static propChangedInfo = new PropertyChangedInfo();
  555. private static propChangeGuarding = false;
  556. protected _handlePropChanged<T>(curValue: T, newValue: T, propName: string, propInfo: Prim2DPropInfo, typeLevelCompare: boolean) {
  557. // Trigger property changed
  558. let info = SmartPropertyBase.propChangeGuarding ? new PropertyChangedInfo() : SmartPropertyPrim.propChangedInfo;
  559. info.oldValue = curValue;
  560. info.newValue = newValue;
  561. info.propertyName = propName;
  562. let propMask = propInfo ? propInfo.flagId : -1;
  563. try {
  564. SmartPropertyBase.propChangeGuarding = true;
  565. this.propertyChanged.notifyObservers(info, propMask);
  566. } finally {
  567. SmartPropertyBase.propChangeGuarding = false;
  568. }
  569. }
  570. protected _triggerPropertyChanged(propInfo: Prim2DPropInfo, newValue: any) {
  571. if (this.isDisposed) {
  572. return;
  573. }
  574. if (!propInfo) {
  575. return;
  576. }
  577. this._handlePropChanged(undefined, newValue, propInfo.name, propInfo, propInfo.typeLevelCompare);
  578. }
  579. /**
  580. * Set the object from which Smart Properties using Binding will take/update their data from/to.
  581. * When the object is part of a graph (with parent/children relationship) if the dataSource of a given instance is not specified, then the parent's one is used.
  582. */
  583. public get dataSource(): IPropertyChanged {
  584. // Don't access to _dataSource directly but via a call to the _getDataSource method which can be overloaded in inherited classes
  585. return this._getDataSource();
  586. }
  587. public set dataSource(value: IPropertyChanged) {
  588. if (this._dataSource === value) {
  589. return;
  590. }
  591. let oldValue = this._dataSource;
  592. this._dataSource = value;
  593. if (this._bindings && value != null) {
  594. // Register the bindings
  595. for (let binding of this._bindings) {
  596. if (binding != null) {
  597. binding._registerDataSource(true);
  598. }
  599. }
  600. }
  601. this.onPropertyChanged("dataSource", oldValue, value);
  602. }
  603. // Inheriting classes can overload this method to provides additional logic for dataSource access
  604. protected _getDataSource(): IPropertyChanged {
  605. return this._dataSource;
  606. }
  607. public createSimpleDataBinding(propInfo: Prim2DPropInfo, propertyPathName: string, mode: number = DataBinding.MODE_DEFAULT): DataBinding {
  608. let binding = new DataBinding();
  609. binding.propertyPathName = propertyPathName;
  610. binding.mode = mode;
  611. return this.createDataBinding(propInfo, binding);
  612. }
  613. public createDataBinding(propInfo: Prim2DPropInfo, binding: DataBinding): DataBinding {
  614. if (!this._bindings) {
  615. this._bindings = new Array<DataBinding>();
  616. }
  617. if (!binding || binding._owner != null) {
  618. throw Error("A valid/unused Binding must be passed.");
  619. }
  620. // Unregister a potentially existing binding for this property
  621. this.removeDataBinding(propInfo);
  622. // register the binding
  623. binding._owner = this;
  624. binding._boundTo = propInfo;
  625. this._bindings[propInfo.id] = binding;
  626. this._hasBinding |= propInfo.flagId;
  627. binding._registerDataSource(true);
  628. return binding;
  629. }
  630. public removeDataBinding(propInfo: Prim2DPropInfo): boolean {
  631. if ((this._hasBinding & propInfo.flagId) === 0) {
  632. return false;
  633. }
  634. let curBinding = this._bindings[propInfo.id];
  635. curBinding._unregisterDataSource();
  636. this._bindings[propInfo.id] = null;
  637. this._hasBinding &= ~propInfo.flagId;
  638. return true;
  639. }
  640. public updateFromDataSource() {
  641. for (let binding of this._bindings) {
  642. if (binding) {
  643. //BindingWatcher.updateFromDataSource(this, binding, false);
  644. }
  645. }
  646. }
  647. private _dataSource: IPropertyChanged;
  648. private _dataSourceObserver: Observer<PropertyChangedInfo>;
  649. private _isDisposed: boolean;
  650. private _externalData: StringDictionary<Object>;
  651. protected _instanceDirtyFlags: number;
  652. private _propInfo: StringDictionary<Prim2DPropInfo>;
  653. public _bindings: Array<DataBinding>;
  654. private _hasBinding: number;
  655. private _bindingSourceChanged: number;
  656. private _disposeObservable: Observable<SmartPropertyBase>;
  657. }
  658. class BindingInfo {
  659. constructor(binding: DataBinding, level: number, isLast: boolean) {
  660. this.binding = binding;
  661. this.level = level;
  662. this.isLast = isLast;
  663. }
  664. binding: DataBinding;
  665. level: number;
  666. isLast: boolean;
  667. }
  668. class MonitoredObjectData {
  669. constructor(monitoredObject: IPropertyChanged) {
  670. this.monitoredObject = monitoredObject;
  671. this.monitoredIntermediateProperties = new StringDictionary<MonitoredObjectData>();
  672. this.observer = this.monitoredObject.propertyChanged.add((e, s) => { this.propertyChangedHandler(e.propertyName, e.oldValue, e.newValue); });
  673. this.boundProperties = new StringDictionary<Array<BindingInfo>>();
  674. this.monitoredIntermediateMask = 0;
  675. this.boundPropertiesMask = 0;
  676. }
  677. monitoredObject: IPropertyChanged;
  678. observer: Observer<PropertyChangedInfo>;
  679. monitoredIntermediateMask: number;
  680. monitoredIntermediateProperties: StringDictionary<MonitoredObjectData>;
  681. boundPropertiesMask;
  682. boundProperties: StringDictionary<Array<BindingInfo>>;
  683. propertyChangedHandler(propName: string, oldValue, newValue) {
  684. let propId = BindingHelper._getPropertyID(this.monitoredObject, propName);
  685. let propIdStr = propId.toString();
  686. // Loop through all the registered bindings for this property that had a value change
  687. if ((this.boundPropertiesMask & propId) !== 0) {
  688. let bindingInfos = this.boundProperties.get(propIdStr);
  689. for (let bi of bindingInfos) {
  690. if (!bi.isLast) {
  691. BindingHelper.unregisterDataSource(this.monitoredObject, bi.binding, bi.level);
  692. BindingHelper.registerDataSource(bi.binding._currentDataSource, bi.binding);
  693. }
  694. if (bi.binding.canUpdateTarget(false)) {
  695. bi.binding.updateTarget();
  696. }
  697. }
  698. }
  699. }
  700. }
  701. class BindingHelper {
  702. static registerDataSource(dataSource: IPropertyChanged, binding: DataBinding) {
  703. let properties = binding.propertyPathName.split(".");
  704. let ownerMod: MonitoredObjectData = null;
  705. let ownerInterPropId = 0;
  706. let propertyOwner = dataSource;
  707. for (let i = 0; i < properties.length; i++) {
  708. let propName = properties[i];
  709. let propId = BindingHelper._getPropertyID(propertyOwner, propName);
  710. let propIdStr = propId.toString();
  711. let mod: MonitoredObjectData;
  712. if (ownerMod) {
  713. let o = ownerMod;
  714. let po = propertyOwner;
  715. let oii = ownerInterPropId;
  716. mod = ownerMod.monitoredIntermediateProperties.getOrAddWithFactory(oii.toString(), k => {
  717. o.monitoredIntermediateMask |= oii;
  718. return BindingHelper._getMonitoredObjectData(po);
  719. });
  720. } else {
  721. mod = BindingHelper._getMonitoredObjectData(propertyOwner);
  722. }
  723. let m = mod;
  724. let bindingInfos = mod.boundProperties.getOrAddWithFactory(propIdStr, k => {
  725. m.boundPropertiesMask |= propId;
  726. return new Array<BindingInfo>();
  727. });
  728. let bi = Tools.first(bindingInfos, cbi => cbi.binding === binding);
  729. if (!bi) {
  730. bindingInfos.push(new BindingInfo(binding, i, (i+1) === properties.length));
  731. }
  732. ownerMod = mod;
  733. ownerInterPropId = propId;
  734. propertyOwner = propertyOwner[propName];
  735. }
  736. }
  737. static unregisterDataSource(dataSource: IPropertyChanged, binding: DataBinding, level: number) {
  738. let properties = binding.propertyPathName.split(".");
  739. let propertyOwner = dataSource;
  740. let mod = BindingHelper._getMonitoredObjectData(propertyOwner);
  741. for (let i = 0; i < properties.length; i++) {
  742. let propName = properties[i];
  743. let propId = BindingHelper._getPropertyID(propertyOwner, propName);
  744. let propIdStr = propId.toString();
  745. if (i >= level) {
  746. mod = BindingHelper._unregisterBinding(mod, propId, binding);
  747. } else {
  748. mod = mod.monitoredIntermediateProperties.get(propIdStr);
  749. }
  750. propertyOwner = propertyOwner[propName];
  751. }
  752. }
  753. private static _unregisterBinding(mod: MonitoredObjectData, propertyID: number, binding: DataBinding): MonitoredObjectData {
  754. let propertyIDStr = propertyID.toString();
  755. let res: MonitoredObjectData = null;
  756. // Check if the property is registered as an intermediate and remove it
  757. if ((mod.monitoredIntermediateMask & propertyID) !== 0) {
  758. res = mod.monitoredIntermediateProperties.get(propertyIDStr);
  759. mod.monitoredIntermediateProperties.remove(propertyIDStr);
  760. // Update the mask
  761. mod.monitoredIntermediateMask &= ~propertyID;
  762. }
  763. // Check if the property is registered as a final property and remove it
  764. if ((mod.boundPropertiesMask & propertyID) !== 0) {
  765. let bindingInfos = mod.boundProperties.get(propertyIDStr);
  766. // Find the binding and remove it
  767. let bi = Tools.first(bindingInfos, cbi => cbi.binding === binding);
  768. if (bi) {
  769. let bii = bindingInfos.indexOf(bi);
  770. bindingInfos.splice(bii, 1);
  771. }
  772. // If the array is empty, update the mask
  773. if (bindingInfos.length === 0) {
  774. mod.boundPropertiesMask &= ~propertyID;
  775. }
  776. }
  777. // Check if the MOD is empty and unregister the observer and remove it from the list of MODs
  778. if (mod.boundPropertiesMask === 0 && mod.monitoredIntermediateMask === 0) {
  779. // Unregister the observer on Property Change
  780. mod.monitoredObject.propertyChanged.remove(mod.observer);
  781. // Remove the MOD from the dic
  782. let objectId = BindingHelper._getObjectId(mod.monitoredObject);
  783. BindingHelper._monitoredObjects.remove(objectId);
  784. }
  785. return res;
  786. }
  787. private static _getMonitoredObjectData(object: IPropertyChanged): MonitoredObjectData {
  788. let objectId = BindingHelper._getObjectId(object);
  789. let mod = BindingHelper._monitoredObjects.getOrAddWithFactory(objectId, k => new MonitoredObjectData(object));
  790. return mod;
  791. }
  792. private static _getObjectId(obj: Object): string {
  793. let id = obj["__bindingHelperObjectId__"];
  794. if (id == null) {
  795. id = Tools.RandomId();
  796. obj["__bindingHelperObjectId__"] = id;
  797. return id;
  798. }
  799. return id;
  800. }
  801. public static _getObjectTypePropertyIDs(obj: IPropertyChanged): StringDictionary<number> {
  802. let fullName = Tools.getFullClassName(obj);
  803. if (!fullName) {
  804. throw Error("Types involved in Data Binding must be decorated with the @className decorator");
  805. }
  806. let d = BindingHelper._propertiesID.getOrAddWithFactory(fullName, () => new StringDictionary<number>());
  807. return d;
  808. }
  809. public static _getPropertyID(object: IPropertyChanged, propName: string): number {
  810. let otd = BindingHelper._getObjectTypePropertyIDs(object);
  811. // Make sure we have a WatchedPropertyData for this property of this object type. This will contains the flagIg of the watched property.
  812. // We use this flagId to flag for each watched instance which properties are watched, as final or intermediate and which directions are used
  813. let propData = otd.getOrAddWithFactory(propName, k => 1 << otd.count);
  814. return propData;
  815. }
  816. private static _propertiesID: StringDictionary<StringDictionary<number>> = new StringDictionary<StringDictionary<number>>();
  817. private static _monitoredObjects: StringDictionary<MonitoredObjectData> = new StringDictionary<MonitoredObjectData>();
  818. }
  819. @className("SmartPropertyPrim", "BABYLON")
  820. /**
  821. * Base class of the primitives, implementing core crosscutting features
  822. */
  823. export abstract class SmartPropertyPrim extends SmartPropertyBase {
  824. static SMARTPROPERTYPRIM_PROPCOUNT: number = 0;
  825. constructor() {
  826. super();
  827. this._flags = 0;
  828. this._uid = null;
  829. this._modelKey = null;
  830. this._levelBoundingInfo = new BoundingInfo2D();
  831. this._boundingInfo = new BoundingInfo2D();
  832. this.animations = new Array<Animation>();
  833. }
  834. /**
  835. * Disposable pattern, this method must be overloaded by derived types in order to clean up hardware related resources.
  836. * @returns false if the object is already dispose, true otherwise. Your implementation must call super.dispose() and check for a false return and return immediately if it's the case.
  837. */
  838. public dispose(): boolean {
  839. if (this.isDisposed) {
  840. return false;
  841. }
  842. super.dispose();
  843. // Don't set to null, it may upset somebody...
  844. this.animations.splice(0);
  845. return true;
  846. }
  847. /**
  848. * Animation array, more info: http://doc.babylonjs.com/tutorials/Animations
  849. */
  850. public animations: Animation[];
  851. /**
  852. * return a unique identifier for the Canvas2D
  853. */
  854. public get uid(): string {
  855. if (!this._uid) {
  856. this._uid = Tools.RandomId();
  857. }
  858. return this._uid;
  859. }
  860. /**
  861. * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
  862. * Look at Sprite2D for more information
  863. */
  864. public getAnimatables(): IAnimatable[] {
  865. return new Array<IAnimatable>();
  866. }
  867. /**
  868. * Property giving the Model Key associated to the property.
  869. * This value is constructed from the type of the primitive and all the name/value of its properties declared with the modelLevelProperty decorator
  870. * @returns the model key string.
  871. */
  872. public get modelKey(): string {
  873. // No need to compute it?
  874. if (!this._isFlagSet(SmartPropertyPrim.flagModelDirty) && this._modelKey) {
  875. return this._modelKey;
  876. }
  877. let modelKey = `Class:${Tools.getClassName(this)};`;
  878. let propDic = this.propDic;
  879. propDic.forEach((k, v) => {
  880. if (v.kind === Prim2DPropInfo.PROPKIND_MODEL) {
  881. let propVal = this[v.name];
  882. // Special case, array, this WON'T WORK IN ALL CASES, all entries have to be of the same type and it must be a BJS well known one
  883. if (propVal && propVal.constructor === Array) {
  884. let firstVal = propVal[0];
  885. if (!firstVal) {
  886. propVal = 0;
  887. } else {
  888. propVal = Tools.hashCodeFromStream(Tools.arrayOrStringFeeder(propVal));
  889. }
  890. }
  891. let value = "[null]";
  892. if (propVal != null) {
  893. if (v.typeLevelCompare) {
  894. value = Tools.getClassName(propVal);
  895. } else {
  896. // String Dictionaries' content are too complex, with use a Random GUID to make the model unique
  897. if (propVal instanceof StringDictionary) {
  898. value = Tools.RandomId();
  899. } else if (propVal instanceof BaseTexture) {
  900. value = propVal.uid;
  901. } else {
  902. value = propVal.toString();
  903. }
  904. }
  905. }
  906. modelKey += v.name + ":" + value + ";";
  907. }
  908. });
  909. this._clearFlags(SmartPropertyPrim.flagModelDirty);
  910. this._modelKey = modelKey;
  911. return modelKey;
  912. }
  913. /**
  914. * States if the Primitive is dirty and should be rendered again next time.
  915. * @returns true is dirty, false otherwise
  916. */
  917. public get isDirty(): boolean {
  918. return (this._instanceDirtyFlags !== 0) || this._areSomeFlagsSet(SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagModelUpdate | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagLayoutDirty);
  919. }
  920. protected _boundingBoxDirty() {
  921. this._setFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
  922. // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
  923. if (this instanceof Prim2DBase) {
  924. let curprim: Prim2DBase = (<any>this);
  925. while (curprim) {
  926. curprim._setFlags(SmartPropertyPrim.flagBoundingInfoDirty);
  927. if (curprim.isSizeAuto) {
  928. curprim.onPrimitivePropertyDirty(Prim2DBase.sizeProperty.flagId);
  929. curprim._setFlags(SmartPropertyPrim.flagPositioningDirty);
  930. }
  931. if (curprim instanceof Group2D) {
  932. if (curprim.isRenderableGroup) {
  933. break;
  934. }
  935. }
  936. curprim = curprim.parent;
  937. }
  938. }
  939. }
  940. protected _handlePropChanged<T>(curValue: T, newValue: T, propName: string, propInfo: Prim2DPropInfo, typeLevelCompare: boolean) {
  941. super._handlePropChanged(curValue, newValue, propName, propInfo, typeLevelCompare);
  942. // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
  943. if (propInfo.dirtyBoundingInfo) {
  944. this._boundingBoxDirty();
  945. } else if (propInfo.dirtyParentBoundingInfo) {
  946. let p: SmartPropertyPrim = (<any>this)._parent;
  947. if (p != null) {
  948. p._boundingBoxDirty();
  949. }
  950. }
  951. // If the property belong to a group, check if it's a cached one, and dirty its render sprite accordingly
  952. if (this instanceof Group2D && (<Group2D><any>this)._renderableData) {
  953. (<SmartPropertyPrim>this).handleGroupChanged(propInfo);
  954. }
  955. // Check for parent layout dirty
  956. if (this instanceof Prim2DBase) {
  957. let p = (<any>this)._parent;
  958. if (p != null && p.layoutEngine && (p.layoutEngine.layoutDirtyOnPropertyChangedMask & propInfo.flagId) !== 0) {
  959. p._setLayoutDirty();
  960. }
  961. let that = this as Prim2DBase;
  962. if (that.layoutEngine && (that.layoutEngine.layoutDirtyOnPropertyChangedMask & propInfo.flagId) !== 0) {
  963. (<any>this)._setLayoutDirty();
  964. }
  965. }
  966. // For type level compare, if there's a change of type it's a change of model, otherwise we issue an instance change
  967. var instanceDirty = false;
  968. if (typeLevelCompare && curValue != null && newValue != null) {
  969. var cvProto = (<any>curValue).__proto__;
  970. var nvProto = (<any>newValue).__proto__;
  971. instanceDirty = (cvProto === nvProto);
  972. }
  973. // Set the dirty flags
  974. if (!instanceDirty && (propInfo.kind === Prim2DPropInfo.PROPKIND_MODEL)) {
  975. if (!this.isDirty) {
  976. this.onPrimBecomesDirty();
  977. this._setFlags(SmartPropertyPrim.flagModelDirty);
  978. }
  979. } else if (instanceDirty || (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) || (propInfo.kind === Prim2DPropInfo.PROPKIND_DYNAMIC)) {
  980. let propMask = propInfo.flagId;
  981. this.onPrimitivePropertyDirty(propMask);
  982. }
  983. }
  984. protected onPrimitivePropertyDirty(propFlagId: number) {
  985. this.onPrimBecomesDirty();
  986. this._instanceDirtyFlags |= propFlagId;
  987. }
  988. protected handleGroupChanged(prop: Prim2DPropInfo) {
  989. }
  990. public _resetPropertiesDirty() {
  991. super._resetPropertiesDirty();
  992. this._clearFlags(SmartPropertyPrim.flagPrimInDirtyList | SmartPropertyPrim.flagNeedRefresh);
  993. }
  994. /**
  995. * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
  996. */
  997. public get levelBoundingInfo(): BoundingInfo2D {
  998. if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
  999. if (this.updateLevelBoundingInfo()) {
  1000. this._boundingInfo.dirtyWorldAABB();
  1001. this._clearFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
  1002. } else {
  1003. this._levelBoundingInfo.clear();
  1004. }
  1005. }
  1006. return this._levelBoundingInfo;
  1007. }
  1008. /**
  1009. * This method must be overridden by a given Primitive implementation to compute its boundingInfo
  1010. */
  1011. protected updateLevelBoundingInfo(): boolean {
  1012. return false;
  1013. }
  1014. /**
  1015. * Property method called when the Primitive becomes dirty
  1016. */
  1017. protected onPrimBecomesDirty() {
  1018. }
  1019. /**
  1020. * Check if a given flag is set
  1021. * @param flag the flag value
  1022. * @return true if set, false otherwise
  1023. */
  1024. public _isFlagSet(flag: number): boolean {
  1025. return (this._flags & flag) !== 0;
  1026. }
  1027. /**
  1028. * Check if all given flags are set
  1029. * @param flags the flags ORed
  1030. * @return true if all the flags are set, false otherwise
  1031. */
  1032. public _areAllFlagsSet(flags: number): boolean {
  1033. return (this._flags & flags) === flags;
  1034. }
  1035. /**
  1036. * Check if at least one flag of the given flags is set
  1037. * @param flags the flags ORed
  1038. * @return true if at least one flag is set, false otherwise
  1039. */
  1040. public _areSomeFlagsSet(flags: number): boolean {
  1041. return (this._flags & flags) !== 0;
  1042. }
  1043. /**
  1044. * Clear the given flags
  1045. * @param flags the flags to clear
  1046. */
  1047. public _clearFlags(flags: number) {
  1048. this._flags &= ~flags;
  1049. }
  1050. /**
  1051. * Set the given flags to true state
  1052. * @param flags the flags ORed to set
  1053. * @return the flags state before this call
  1054. */
  1055. public _setFlags(flags: number): number {
  1056. let cur = this._flags;
  1057. this._flags |= flags;
  1058. return cur;
  1059. }
  1060. /**
  1061. * Change the state of the given flags
  1062. * @param flags the flags ORed to change
  1063. * @param state true to set them, false to clear them
  1064. */
  1065. public _changeFlags(flags: number, state: boolean) {
  1066. if (state) {
  1067. this._flags |= flags;
  1068. } else {
  1069. this._flags &= ~flags;
  1070. }
  1071. }
  1072. public static flagNoPartOfLayout = 0x0000001; // set if the primitive's position/size must not be computed by Layout Engine
  1073. public static flagLevelBoundingInfoDirty = 0x0000002; // set if the primitive's level bounding box (not including children) is dirty
  1074. public static flagModelDirty = 0x0000004; // set if the model must be changed
  1075. public static flagLayoutDirty = 0x0000008; // set if the layout must be computed
  1076. public static flagLevelVisible = 0x0000010; // set if the primitive is set as visible for its level only
  1077. public static flagBoundingInfoDirty = 0x0000020; // set if the primitive's overall bounding box (including children) is dirty
  1078. public static flagIsPickable = 0x0000040; // set if the primitive can be picked during interaction
  1079. public static flagIsVisible = 0x0000080; // set if the primitive is concretely visible (use the levelVisible of parents)
  1080. public static flagVisibilityChanged = 0x0000100; // set if there was a transition between visible/hidden status
  1081. public static flagPositioningDirty = 0x0000200; // set if the primitive positioning must be computed
  1082. public static flagTrackedGroup = 0x0000400; // set if the group2D is tracking a scene node
  1083. public static flagWorldCacheChanged = 0x0000800; // set if the cached bitmap of a world space canvas changed
  1084. public static flagChildrenFlatZOrder = 0x0001000; // set if all the children (direct and indirect) will share the same Z-Order
  1085. public static flagZOrderDirty = 0x0002000; // set if the Z-Order for this prim and its children must be recomputed
  1086. public static flagActualOpacityDirty = 0x0004000; // set if the actualOpactity should be recomputed
  1087. public static flagPrimInDirtyList = 0x0008000; // set if the primitive is in the primDirtyList
  1088. public static flagIsContainer = 0x0010000; // set if the primitive is a container
  1089. public static flagNeedRefresh = 0x0020000; // set if the primitive wasn't successful at refresh
  1090. public static flagActualScaleDirty = 0x0040000; // set if the actualScale property needs to be recomputed
  1091. public static flagDontInheritParentScale = 0x0080000; // set if the actualScale must not use its parent's scale to be computed
  1092. public static flagGlobalTransformDirty = 0x0100000; // set if the global transform must be recomputed due to a local transform change
  1093. public static flagLayoutBoundingInfoDirty = 0x0200000; // set if the layout bounding info is dirty
  1094. public static flagCollisionActor = 0x0400000; // set if the primitive is part of the collision engine
  1095. public static flagModelUpdate = 0x0800000; // set if the primitive's model data is to update
  1096. private _uid : string;
  1097. private _flags : number;
  1098. private _modelKey : string;
  1099. protected _levelBoundingInfo : BoundingInfo2D;
  1100. protected _boundingInfo : BoundingInfo2D;
  1101. protected _layoutBoundingInfo : BoundingInfo2D;
  1102. }
  1103. export function dependencyProperty<T>(propId: number, piStore: (pi: Prim2DPropInfo) => void, mode = DataBinding.MODE_TWOWAY, updateSourceTrigger = DataBinding.UPDATESOURCETRIGGER_PROPERTYCHANGED): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {
  1104. return SmartPropertyBase._hookProperty(propId, piStore, Prim2DPropInfo.PROPKIND_DYNAMIC, { bindingMode: mode, bindingUpdateSourceTrigger: updateSourceTrigger });
  1105. }
  1106. export function modelLevelProperty<T>(propId: number, piStore: (pi: Prim2DPropInfo) => void, typeLevelCompare = false, dirtyBoundingInfo = false, dirtyParentBoundingBox = false): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {
  1107. return SmartPropertyBase._hookProperty(propId, piStore, Prim2DPropInfo.PROPKIND_MODEL, { typeLevelCompare: typeLevelCompare, dirtyBoundingInfo: dirtyBoundingInfo, dirtyParentBoundingBox: dirtyParentBoundingBox });
  1108. }
  1109. export function instanceLevelProperty<T>(propId: number, piStore: (pi: Prim2DPropInfo) => void, typeLevelCompare = false, dirtyBoundingInfo = false, dirtyParentBoundingBox = false): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {
  1110. return SmartPropertyBase._hookProperty(propId, piStore, Prim2DPropInfo.PROPKIND_INSTANCE, { typeLevelCompare: typeLevelCompare, dirtyBoundingInfo: dirtyBoundingInfo, dirtyParentBoundingBox: dirtyParentBoundingBox });
  1111. }
  1112. export function dynamicLevelProperty<T>(propId: number, piStore: (pi: Prim2DPropInfo) => void, typeLevelCompare = false, dirtyBoundingInfo = false, dirtyParentBoundingBox = false): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {
  1113. return SmartPropertyBase._hookProperty(propId, piStore, Prim2DPropInfo.PROPKIND_DYNAMIC, { typeLevelCompare: typeLevelCompare, dirtyBoundingInfo: dirtyBoundingInfo, dirtyParentBoundingBox: dirtyParentBoundingBox });
  1114. }
  1115. }