diff --git a/tests/app/TKUnit.ts b/tests/app/TKUnit.ts index 7a237353a..761724d7b 100644 --- a/tests/app/TKUnit.ts +++ b/tests/app/TKUnit.ts @@ -66,7 +66,7 @@ var runTest = function (testInfo: TestInfoEntry) { if (testInfo.isTest) { duration = time() - start; testInfo.duration = duration; - write(`--- [${testInfo.testName}] FAILED: ${e.message}, duration: ${duration}`, trace.messageType.error); + write(`--- [${testInfo.testName}] FAILED: ${e.message}, Stack: ${e.stack}, duration: ${duration}`, trace.messageType.error); testInfo.isPassed = false; testInfo.errorMessage = e.message; } diff --git a/tests/app/ui/dependency-observable-tests.ts b/tests/app/ui/dependency-observable-tests.ts index cf8483610..26c9a5067 100644 --- a/tests/app/ui/dependency-observable-tests.ts +++ b/tests/app/ui/dependency-observable-tests.ts @@ -225,9 +225,9 @@ export function test_PropertyMetadata_Options() { var p = new dependencyObservableModule.Property(generatePropertyName(), "testOwner", new dependencyObservableModule.PropertyMetadata(0, options)); - TKUnit.assert(p.metadata.affectsLayout === true, "PropertyMetadata.affectsLayout not working as expected."); - TKUnit.assert(p.metadata.affectsStyle === true, "PropertyMetadata.affectsStyle not working as expected."); - TKUnit.assert(p.metadata.inheritable === true, "PropertyMetadata.inheritable not working as expected."); + TKUnit.assert(p.affectsLayout === true, "Property.affectsLayout not working as expected."); + TKUnit.assert(p.affectsStyle === true, "Property.affectsStyle not working as expected."); + TKUnit.assert(p.inheritable === true, "Property.inheritable not working as expected."); TKUnit.assert(p.metadata.options === options, "PropertyMetadata.options not properly set."); } @@ -236,34 +236,16 @@ export function test_PropertyEntry_effectiveValue() { var entry = new dependencyObservableModule.PropertyEntry(p); // default value - TKUnit.assert(entry.effectiveValue === p.metadata.defaultValue, "ValueSource.Default not working as expected."); - TKUnit.assert(entry.valueSource === dependencyObservableModule.ValueSource.Default, "ValueSource.Default not working as expected."); - - // inherited value - entry.inheritedValue = 5; - TKUnit.assert(entry.effectiveValue === 5, "PropertyEntry.inheritedValue not working as expected."); - TKUnit.assert(entry.valueSource === dependencyObservableModule.ValueSource.Inherited, "ValueSource.Inherited not working as expected."); + TKUnit.assertEqual(entry.effectiveValue, undefined, "effectiveValue should be undefined."); + TKUnit.assertEqual(entry.valueSource, dependencyObservableModule.ValueSource.Default, "ValueSource.Default not working as expected."); // local value entry.localValue = 10; - TKUnit.assert(entry.effectiveValue === 10, "PropertyEntry.localValue not working as expected."); - TKUnit.assert(entry.valueSource === dependencyObservableModule.ValueSource.Local, "ValueSource.Local not working as expected."); - - // the default Property implementation counts the above three modifier ONLY, it should skip the Css and VisualState ones - // css value - entry.cssValue = 20; - TKUnit.assert(entry.effectiveValue === 10, "PropertyEntry.cssValue should work for Style properties ONLY."); - TKUnit.assert(entry.valueSource === dependencyObservableModule.ValueSource.Local, "ValueSource.Local not working as expected."); - - // visual state value - entry.visualStateValue = 30; - TKUnit.assert(entry.effectiveValue === 10, "PropertyEntry.visualStateValue should work for Style properties ONLY."); - TKUnit.assert(entry.valueSource === dependencyObservableModule.ValueSource.Local, "ValueSource.Local not working as expected."); - entry.resetValue(); + // default value - TKUnit.assert(entry.effectiveValue === p.metadata.defaultValue, "ValueSource.Default not working as expected."); - TKUnit.assert(entry.valueSource === dependencyObservableModule.ValueSource.Default, "ValueSource.Default not working as expected."); + TKUnit.assertEqual(entry.effectiveValue, undefined, "ValueSource.Default not working as expected."); + TKUnit.assertEqual(entry.valueSource, dependencyObservableModule.ValueSource.Default, "ValueSource.Default not working as expected."); } export function test_DependencyObservable_get_set_AreOverriden() { @@ -281,29 +263,29 @@ export function test_DependencyObservable_getValue_setValue() { // implicitly use ValueSource.Local when not specified dO._setValue(testProperty1, 5); - TKUnit.assert(dO.test1 === 5, "expected true after _setValue."); - TKUnit.assert(dO._getValueSource(testProperty1) === dependencyObservableModule.ValueSource.Local, "_setValue without third parameter should use ValueSource.Local"); + TKUnit.assertEqual(dO.test1, 5, "expected true after _setValue."); + TKUnit.assertEqual(dO._getValueSource(testProperty1), dependencyObservableModule.ValueSource.Local, "_setValue without third parameter should use ValueSource.Local"); dO._setValue(testProperty1, 10, dependencyObservableModule.ValueSource.Inherited); - TKUnit.assert(dO.test1 === 5, "ValueSource.Local should have higher priority than Inherited."); + TKUnit.assertEqual(dO.test1, 5, "ValueSource.Local should have higher priority than Inherited."); // revert to default value dO._resetValue(testProperty1); - TKUnit.assert(dO.test1 === testProperty1.metadata.defaultValue, "_resetValue should revert to metadata.defaultValue"); + TKUnit.assertEqual(dO.test1, 10, "_resetValue should revert to inheritedValue"); // test the ValueSource parameter dO._setValue(testProperty1, 10, dependencyObservableModule.ValueSource.Inherited); - TKUnit.assert(dO.test1 === 10, "_setValue with three parameters not working as expected."); - TKUnit.assert(dO._getValueSource(testProperty1) === dependencyObservableModule.ValueSource.Inherited, "expected ValueSource.Inherited"); + TKUnit.assertEqual(dO.test1, 10, "_setValue with three parameters not working as expected."); + TKUnit.assertEqual(dO._getValueSource(testProperty1), dependencyObservableModule.ValueSource.Inherited, "expected ValueSource.Inherited"); dO._setValue(testProperty1, 20, dependencyObservableModule.ValueSource.Local); - TKUnit.assert(dO.test1 === 20, "_setValue with three parameters not working as expected."); - TKUnit.assert(dO._getValueSource(testProperty1) === dependencyObservableModule.ValueSource.Local, "expected ValueSource.Local"); + TKUnit.assertEqual(dO.test1, 20, "_setValue with three parameters not working as expected."); + TKUnit.assertEqual(dO._getValueSource(testProperty1), dependencyObservableModule.ValueSource.Local, "expected ValueSource.Local"); // reset the Local value and verify the effectiveValue will be reverted to the Inherited one. dO._resetValue(testProperty1, dependencyObservableModule.ValueSource.Local); - TKUnit.assert(dO.test1 === 10, "_resetValue for ValueSource not working as expected."); - TKUnit.assert(dO._getValueSource(testProperty1) === dependencyObservableModule.ValueSource.Inherited, "expected ValueSource.Inherited"); + TKUnit.assertEqual(dO.test1, 10, "_resetValue for ValueSource not working as expected."); + TKUnit.assertEqual(dO._getValueSource(testProperty1), dependencyObservableModule.ValueSource.Inherited, "expected ValueSource.Inherited"); } export function test_PropertyMetadata_onValueChanged_Callback_IsInvoked() { diff --git a/tests/app/ui/scroll-view/scroll-view-tests.ts b/tests/app/ui/scroll-view/scroll-view-tests.ts index a23cb00c5..cb62b2c1a 100644 --- a/tests/app/ui/scroll-view/scroll-view-tests.ts +++ b/tests/app/ui/scroll-view/scroll-view-tests.ts @@ -37,9 +37,10 @@ class ScrollLayoutTest extends testModule.UITest { } public test_default_TNS_values() { - TKUnit.assertEqual(this.testView.orientation, enums.Orientation.vertical, "Default this.testView.orientation"); - TKUnit.assertEqual(this.testView.verticalOffset, 0, "Default this.testView.verticalOffset"); - TKUnit.assertEqual(this.testView.horizontalOffset, 0, "Default this.testView.horizontalOffset"); + let scroll = new scrollViewModule.ScrollView(); + TKUnit.assertEqual(scroll.orientation, enums.Orientation.vertical, "Default this.testView.orientation"); + TKUnit.assertEqual(scroll.verticalOffset, 0, "Default this.testView.verticalOffset"); + TKUnit.assertEqual(scroll.horizontalOffset, 0, "Default this.testView.horizontalOffset"); } public test_vertical_oriantation_creates_correct_native_view() { diff --git a/tests/package.json b/tests/package.json index 72443af9e..5ff8662c8 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,25 +1,25 @@ { - "description": "NativeScript Application", - "license": "SEE LICENSE IN ", - "readme": "NativeScript Application", - "repository": "", - "nativescript": { - "id": "org.nativescript.tests", - "tns-ios": { - "version": "2.0.0" - }, - "tns-android": { - "version": "2.0.0" - } - }, - "dependencies": { - "tns-core-modules": "2.0.1" - }, - "devDependencies": { - "babel-traverse": "6.9.0", - "babel-types": "6.9.0", - "babylon": "6.8.0", - "filewalker": "0.1.2", - "lazy": "1.0.11" - } -} \ No newline at end of file + "description": "NativeScript Application", + "license": "SEE LICENSE IN ", + "readme": "NativeScript Application", + "repository": "", + "nativescript": { + "id": "org.nativescript.tests", + "tns-ios": { + "version": "2.0.0" + }, + "tns-android": { + "version": "2.0.0" + } + }, + "dependencies": { + "tns-core-modules": "2.0.1" + }, + "devDependencies": { + "babel-traverse": "6.9.0", + "babel-types": "6.9.0", + "babylon": "6.8.0", + "filewalker": "0.1.2", + "lazy": "1.0.11" + } +} diff --git a/tns-core-modules/ui/button/button.ios.ts b/tns-core-modules/ui/button/button.ios.ts index c14f1eba1..b500aad9e 100644 --- a/tns-core-modules/ui/button/button.ios.ts +++ b/tns-core-modules/ui/button/button.ios.ts @@ -61,7 +61,7 @@ export class Button extends common.Button { public _onPropertyChanged(property: Property, oldValue: any, newValue: any) { super._onPropertyChanged(property, oldValue, newValue); - if (property.metadata.affectsStyle) { + if (property.affectsStyle) { this._updateHandler(); } } diff --git a/tns-core-modules/ui/core/bindable.ts b/tns-core-modules/ui/core/bindable.ts index a163003ba..e16e390c1 100644 --- a/tns-core-modules/ui/core/bindable.ts +++ b/tns-core-modules/ui/core/bindable.ts @@ -1,6 +1,6 @@ import definition = require("ui/core/bindable"); import {Observable, PropertyChangeData} from "data/observable"; -import {DependencyObservable, Property, PropertyMetadata, PropertyMetadataSettings, PropertyChangeData as DependencyPropertyChangeData} from "ui/core/dependency-observable"; +import {unsetValue, DependencyObservable, Property, PropertyMetadata, PropertyMetadataSettings, PropertyChangeData as DependencyPropertyChangeData} from "ui/core/dependency-observable"; import weakEvents = require("ui/core/weak-event-listener"); import types = require("utils/types"); import trace = require("trace"); @@ -60,9 +60,9 @@ export class Bindable extends DependencyObservable implements definition.Bindabl binding.sourceIsBindingContext = true; } - if (!types.isNullOrUndefined(bindingSource)) { - binding.bind(bindingSource); - } + // if (!types.isNullOrUndefined(bindingSource)) { + binding.bind(bindingSource); + // } } public unbind(property: string) { @@ -91,7 +91,7 @@ export class Bindable extends DependencyObservable implements definition.Bindabl } super._onPropertyChanged(property, oldValue, newValue); if (this instanceof viewModule.View) { - if (property.metadata.inheritable && ((this))._isInheritedChange() === true) { + if (property.inheritable && ((this))._isInheritedChange() === true) { return; } } @@ -200,7 +200,7 @@ export class Binding { this.sourceOptions = undefined; } } - + private sourceAsObject(source: any): any { /* tslint:disable */ let objectType = typeof source; @@ -521,7 +521,7 @@ export class Binding { return; } - this.updateOptions(this.targetOptions, value); + this.updateOptions(this.targetOptions, types.isNullOrUndefined(value) ? unsetValue : value); } private updateSource(value: any) { @@ -619,4 +619,4 @@ export class Binding { this.updating = false; } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/core/dependency-observable.d.ts b/tns-core-modules/ui/core/dependency-observable.d.ts index 6b9acb536..4eb3d6938 100644 --- a/tns-core-modules/ui/core/dependency-observable.d.ts +++ b/tns-core-modules/ui/core/dependency-observable.d.ts @@ -4,6 +4,10 @@ declare module "ui/core/dependency-observable" { import observable = require("data/observable"); + /** + * Value specifing that Property value should be reset. Used when bindingContext on bound property is creared/null. + */ + export let unsetValue: Object; /** * Interface used by Propery 'defaultValueGetter' function to specify if the default value returned by the native instance can be cached or not. * One example is - android.widget.Button background. It is state drawable so it cannot be reused/cached. @@ -17,6 +21,7 @@ declare module "ui/core/dependency-observable" { * Represents a special Property which supports changed callback, metadata and value validation. */ export class Property { + /** * Creates a new Property instance. * @param name The name of the property. @@ -25,17 +30,17 @@ declare module "ui/core/dependency-observable" { * @param valueConverter A function that can create an instance of the property type given a string. Used when parsing CSS and XML attribute values which are strings. */ constructor(name: string, ownerType: string, metadata: PropertyMetadata, valueConverter?: (value: string) => any); - + /** * Gets the name of the property. This is a read-only property. */ name: string; - + /** * Gets the id of the property. This is used for fast lookup. This is a read-only property. */ id: number; - + /** * Gets the PropertyMetadata object associated with the property. This is a read-only property. */ @@ -51,6 +56,14 @@ declare module "ui/core/dependency-observable" { * If default value is 'undefined' and this property is set this function will be used to extract the default value from the native instance. */ defaultValueGetter: (instance: DependencyObservable) => NativeValueResult; + + defaultValue: any; + onValueChanged: PropertyChangedCallback; + onValidateValue: PropertyValidationCallback; + equalityComparer: PropertyEqualityComparer; + affectsLayout: boolean; + inheritable: boolean; + affectsStyle: boolean; } /** @@ -68,7 +81,7 @@ declare module "ui/core/dependency-observable" { constructor( defaultValue: any, options?: number, - onChanged?: PropertyChangedCallback, + onChanged?: PropertyChangedCallback, onValidateValue?: PropertyValidationCallback, equalityComparer?: PropertyEqualityComparer); diff --git a/tns-core-modules/ui/core/dependency-observable.ts b/tns-core-modules/ui/core/dependency-observable.ts index 18190b77c..b820cbb07 100644 --- a/tns-core-modules/ui/core/dependency-observable.ts +++ b/tns-core-modules/ui/core/dependency-observable.ts @@ -5,6 +5,7 @@ import types = require("utils/types"); // use private variables in the scope of the module rather than static members of the class since a member is still accessible through JavaScript and may be changed. var propertyFromKey = {}; var propertyIdCounter = 0; +export let unsetValue = new Object(); function generatePropertyKey(name: string, ownerType: string, validate?: boolean) { if (validate) { @@ -54,210 +55,107 @@ export module ValueSource { } export class PropertyMetadata implements definition.PropertyMetadata { - private _defaultValue: any; - private _options: number; - private _onChanged: definition.PropertyChangedCallback; - private _onValidateValue: definition.PropertyValidationCallback; - private _equalityComparer: definition.PropertyEqualityComparer; + public inheritable: boolean; + public affectsStyle: boolean; + public affectsLayout: boolean; + public onValueChanged: definition.PropertyChangedCallback; constructor( - defaultValue: any, - options?: number, + public defaultValue: any, + public options: number = PropertyMetadataSettings.None, onChanged?: definition.PropertyChangedCallback, - onValidateValue?: definition.PropertyValidationCallback, - equalityComparer?: definition.PropertyEqualityComparer) { - this._defaultValue = defaultValue; - this._options = options; - if (types.isNullOrUndefined(this._options)) { - this._options = PropertyMetadataSettings.None; - } - this._onChanged = onChanged; - this._onValidateValue = onValidateValue; - this._equalityComparer = equalityComparer; - } - - public get defaultValue(): any { - return this._defaultValue; - } - - public get options(): number { - return this._options; - } - - public get onValueChanged(): definition.PropertyChangedCallback { - return this._onChanged; - } - public set onValueChanged(value: definition.PropertyChangedCallback) { - this._onChanged = value; - } - - public get onValidateValue(): definition.PropertyValidationCallback { - return this._onValidateValue; - } - - public get equalityComparer(): definition.PropertyEqualityComparer { - return this._equalityComparer; - } - - public get affectsLayout(): boolean { - return (this._options & PropertyMetadataSettings.AffectsLayout) === PropertyMetadataSettings.AffectsLayout; - } - - public get affectsStyle(): boolean { - return (this._options & PropertyMetadataSettings.AffectsStyle) === PropertyMetadataSettings.AffectsStyle; - } - - public get inheritable(): boolean { - return (this._options & PropertyMetadataSettings.Inheritable) === PropertyMetadataSettings.Inheritable; + public onValidateValue?: definition.PropertyValidationCallback, + public equalityComparer?: definition.PropertyEqualityComparer) { + this.defaultValue = defaultValue; + this.options = options; + this.onValueChanged = onChanged; + this.onValidateValue = onValidateValue; + this.equalityComparer = equalityComparer; + this.inheritable = (options & PropertyMetadataSettings.Inheritable) === PropertyMetadataSettings.Inheritable; + this.affectsStyle = (options & PropertyMetadataSettings.AffectsStyle) === PropertyMetadataSettings.AffectsStyle; + this.affectsLayout = (options & PropertyMetadataSettings.AffectsLayout) === PropertyMetadataSettings.AffectsLayout; } } +// function property(property: Property): PropertyDecorator { +// function _getValue() { +// return this._getValue(property); +// } + +// function _setValue(value: any) { +// this._setValueInternal(property, value, ValueSource.Local); +// } + +// return (target: Object, propertyKey: string) => { +// Object.defineProperty(target, propertyKey, { +// get: _getValue, +// set: _setValue, +// enumerable: true, +// configurable: true +// }); +// }; +// } + export class Property implements definition.Property { - private _metadata: PropertyMetadata; - private _key: string; - private _name: string; - private _ownerType: string; - private _id: number; - private _valueConverter: (value: any) => any; - - constructor(name: string, ownerType: string, metadata: PropertyMetadata, valueConverter?: (value: string) => any) { + public key: string; + public id: number; + public defaultValue; + public onValidateValue; + public equalityComparer; + public inheritable; + public affectsStyle; + public affectsLayout; + public onValueChanged: definition.PropertyChangedCallback; + public nameEvent: string; + constructor(public name: string, public ownerType: string, public metadata: PropertyMetadata, public valueConverter?: (value: string) => any) { // register key - this._key = generatePropertyKey(name, ownerType, true); + this.key = generatePropertyKey(name, ownerType, true); - if (propertyFromKey[this._key]) { + if (propertyFromKey[this.key]) { throw new Error("Property " + name + " already registered for type " + ownerType + "."); } - propertyFromKey[this._key] = this; + propertyFromKey[this.key] = this; if (!metadata || !(metadata instanceof PropertyMetadata)) { throw new Error("Expected valid PropertyMetadata instance."); } - this._name = name; - this._ownerType = ownerType; - this._metadata = metadata; + this.name = name; + this.nameEvent = name + "Change"; + this.ownerType = ownerType; + this.metadata = metadata; // generate a unique numeric id for each property (faster lookup than a string key) - this._id = propertyIdCounter++; - this._valueConverter = valueConverter; + this.id = propertyIdCounter++; + this.valueConverter = valueConverter; + this.defaultValue = metadata.defaultValue; + this.onValueChanged = metadata.onValueChanged; + this.onValidateValue = metadata.onValidateValue; + this.equalityComparer = metadata.equalityComparer || ((x, y) => x === y); + this.inheritable = metadata.inheritable; + this.affectsStyle = metadata.affectsStyle; + this.affectsLayout = metadata.affectsLayout; } public defaultValueGetter: (instance: definition.DependencyObservable) => definition.NativeValueResult; - - public get name(): string { - return this._name; - } - - public get id(): number { - return this._id; - } - - public get metadata(): PropertyMetadata { - return this._metadata; - } - - public isValidValue(value: Object): boolean { - if (this.metadata.onValidateValue) { - return this.metadata.onValidateValue(value); - } - - // TODO: consider type check here (e.g. passing function where object is expected) - return true; - } - - public get valueConverter(): (value: string) => any { - return this._valueConverter; - } - - public _getEffectiveValue(entry: PropertyEntry): any { - if (types.isDefined(entry.localValue)) { - entry.valueSource = ValueSource.Local; - return entry.localValue; - } - - if (types.isDefined(entry.inheritedValue)) { - entry.valueSource = ValueSource.Inherited; - return entry.inheritedValue; - } - - entry.valueSource = ValueSource.Default; - return this.metadata.defaultValue; - } } export class PropertyEntry implements definition.PropertyEntry { - private _property: Property; + public valueSource: number = ValueSource.Default; + public inheritedValue: any; + public cssValue: any; + public localValue: any; + public effectiveValue: any; + public visualStateValue: any; - private _valueSource: number; - private _inheritedValue: any; - private _cssValue: any; - private _localValue: any; - private _effectiveValue: any; - private _visualStateValue: any; - - constructor(property: Property) { - this._property = property; - } - - get property(): Property { - return this._property; - } - - get effectiveValue() { - if (!this._effectiveValue) { - this._effectiveValue = this._property._getEffectiveValue(this); - } - - return this._effectiveValue; - } - - get valueSource(): number { - return this._valueSource; - } - set valueSource(value: number) { - this._valueSource = value; - } - - get localValue(): any { - return this._localValue; - } - set localValue(value: any) { - this._localValue = value; - this._effectiveValue = undefined; - } - - get inheritedValue(): any { - return this._inheritedValue; - } - set inheritedValue(value: any) { - this._inheritedValue = value; - this._effectiveValue = undefined; - } - - get cssValue(): any { - return this._cssValue; - } - set cssValue(value: any) { - this._cssValue = value; - this._effectiveValue = undefined; - } - - get visualStateValue(): any { - return this._visualStateValue; - } - set visualStateValue(value: any) { - this._visualStateValue = value; - this._effectiveValue = undefined; + constructor(public property: Property) { + this.property = property; } public resetValue() { - this._valueSource = ValueSource.Default; - this._visualStateValue = undefined; - this._localValue = undefined; - this._cssValue = undefined; - this._inheritedValue = undefined; - this._effectiveValue = undefined; + this.valueSource = ValueSource.Default; + this.inheritedValue = this.cssValue = this.localValue = this.visualStateValue = this.effectiveValue = undefined; } } @@ -269,7 +167,7 @@ export class DependencyObservable extends Observable implements definition.Depen public set(name: string, value: any) { var property = getPropertyByNameAndType(name, this); if (property) { - this._setValue(property, value, ValueSource.Local); + this._setValueInternal(property, value, ValueSource.Local); } else { super.set(name, value); } @@ -285,16 +183,7 @@ export class DependencyObservable extends Observable implements definition.Depen } public _setValue(property: Property, value: any, source?: number) { - let realValue = WrappedValue.unwrap(value); - if (!property.isValidValue(realValue)) { - throw new Error("Invalid value " + realValue + " for property " + property.name); - } - - if (types.isUndefined(source)) { - source = ValueSource.Local; - } - - this._setValueInternal(property, value, source); + this._setValueInternal(property, value, source || ValueSource.Local); } public _getValueSource(property: Property): number { @@ -311,12 +200,18 @@ export class DependencyObservable extends Observable implements definition.Depen if (entry) { return entry.effectiveValue; } - else if (property.defaultValueGetter) { // we check for cached properties only for these which have 'defaultValueGetter' defined; + else { + return this._getDefaultValue(property); + } + } + + private _getDefaultValue(property: Property): any { + if (property.defaultValueGetter) { // we check for cached properties only for these which have 'defaultValueGetter' defined; // When DependencyProperties are removed from Style - fix this check. var view = (this)._view || this; let key = types.getClass(view) + "." + property.id; let defaultValue = defaultValueForPropertyPerType.get(key); - if (types.isUndefined(defaultValue) && view._nativeView) { + if (!defaultValueForPropertyPerType.has(key) && view._nativeView) { let defaultValueResult = property.defaultValueGetter(this); defaultValue = defaultValueResult.result; if (defaultValueResult.cacheable) { @@ -327,70 +222,101 @@ export class DependencyObservable extends Observable implements definition.Depen return defaultValue; } - return property.metadata.defaultValue; + return property.defaultValue; } - public _resetValue(property: Property, source?: number) { - if (!(property.id in this._propertyEntries)) { + public _resetValue(property: Property, source: number = ValueSource.Local) { + let entry: PropertyEntry = this._propertyEntries[property.id]; + if (!entry) { return; } - if (types.isDefined(source)) { - // resetting particular modifier to undefined will remove it from the effective value composition - this._setValueInternal(property, undefined, source); - } else { - var currentValue = this._getValue(property); - delete this._propertyEntries[property.id]; - var newValue = this._getValue(property); - - var comparer: (x: any, y: any) => boolean = property.metadata.equalityComparer || this._defaultComparer; - if (!comparer(currentValue, newValue)) { - this._onPropertyChanged(property, currentValue, newValue); - } + switch (source) { + case ValueSource.Inherited: + entry.inheritedValue = undefined; + break; + case ValueSource.Css: + entry.cssValue = undefined; + break; + case ValueSource.Local: + entry.localValue = undefined; + break; + case ValueSource.VisualState: + entry.visualStateValue = undefined; + break; } + + let currentValueSource = entry.valueSource; + if (currentValueSource !== source) { + // If current valueSource is larget than the one we reset - do nothing. + // We are reseting property will lower priority and it won't change effectValue; + // Reseting larger source means we somehow was able to set value without updating currentValueSource which is clearly a bug. + return; + } + + let currentValue = entry.effectiveValue; + let newValue = this.getEffectiveValue(currentValueSource, entry, property); + if (!property.equalityComparer(currentValue, newValue)) { + // If we fallback to defalutValue - remove propertyEntry. + if (entry.valueSource === ValueSource.Default) { + delete this._propertyEntries[property.id]; + } + else { + entry.effectiveValue = newValue; + } + + this._onPropertyChanged(property, currentValue, newValue); + } + + // if (types.isDefined(source)) { + // // resetting particular modifier to undefined will remove it from the effective value composition + // this._resetValueInternal(property, source); + // } else { + // let currentValue = entry.effectiveValue; + // delete this._propertyEntries[property.id]; + // let newValue = this._getDefaultValue(property); + // if (!property.equalityComparer(currentValue, newValue)) { + // this._onPropertyChanged(property, currentValue, newValue); + // } + // } } public _onPropertyChanged(property: Property, oldValue: any, newValue: any) { - let realNewValue = WrappedValue.unwrap(newValue); - if (property.metadata.onValueChanged) { - property.metadata.onValueChanged({ + // let realNewValue = WrappedValue.unwrap(newValue); + let valueChanged = property.onValueChanged; + if (valueChanged) { + valueChanged({ object: this, property: property, eventName: Observable.propertyChangeEvent, - newValue: realNewValue, + newValue: newValue, oldValue: oldValue }); } + let propName = property.name; if (this.hasListeners(Observable.propertyChangeEvent)) { - var changeData = super._createPropertyChangeData(property.name, newValue); + let changeData = super._createPropertyChangeData(propName, newValue); this.notify(changeData); } - let eventName = property.name + "Change"; + let eventName = property.nameEvent; if (this.hasListeners(eventName)) { - var ngChangedData = { + let ngChangedData = { eventName: eventName, - propertyName: property.name, + propertyName: propName, object: this, - value: realNewValue + value: newValue } this.notify(ngChangedData); } } public _eachSetProperty(callback: (property: Property) => boolean) { - var i; - var key; - var entry: PropertyEntry; - var retVal: boolean; - var keys = Object.keys(this._propertyEntries); - - for (i = 0; i < keys.length; i++) { - key = keys[i]; - entry = this._propertyEntries[key]; - retVal = callback(entry.property); - if (!retVal) { + for (let i = 0, keys = Object.keys(this._propertyEntries); i < keys.length; i++) { + let key = keys[i]; + let entry = this._propertyEntries[key]; + if (!callback(entry.property)) { break; } } @@ -401,27 +327,47 @@ export class DependencyObservable extends Observable implements definition.Depen } private _setValueInternal(property: Property, value: any, source: number) { - let realValue = WrappedValue.unwrap(value); - // Convert the value to the real property type in case it is coming as a string from CSS or XML. - if (types.isString(realValue) && property.valueConverter) { - realValue = property.valueConverter(realValue); + if (value === unsetValue) { + this._resetValue(property, source); + return; } - var entry: PropertyEntry = this._propertyEntries[property.id]; + let wrapped = value && value.wrapped; + let realValue = wrapped ? WrappedValue.unwrap(value) : value; + let validate = property.onValidateValue; + if (validate && !validate(realValue)) { + throw new Error("Invalid value " + realValue + " for property " + property.name); + } + + // Convert the value to the real property type in case it is coming as a string from CSS or XML. + let converter = property.valueConverter; + if (converter && types.isString(realValue)) { + realValue = converter(realValue); + } + + let entry: PropertyEntry = this._propertyEntries[property.id]; + let currentValue; if (!entry) { entry = new PropertyEntry(property); this._propertyEntries[property.id] = entry; + currentValue = this._getDefaultValue(property); + // In rare case when we set local value equal to default value we need to update effectiveValue as well. + // Otherwise effectiveValue will stay undefined. + if (property.equalityComparer(currentValue, realValue)) { + entry.effectiveValue = realValue; + } + } + else { + currentValue = entry.effectiveValue; } - var currentValue = entry.effectiveValue; - switch (source) { - case ValueSource.Css: - entry.cssValue = realValue; - break; case ValueSource.Inherited: entry.inheritedValue = realValue; break; + case ValueSource.Css: + entry.cssValue = realValue; + break; case ValueSource.Local: entry.localValue = realValue; break; @@ -430,13 +376,78 @@ export class DependencyObservable extends Observable implements definition.Depen break; } - var comparer: (x: any, y: any) => boolean = property.metadata.equalityComparer || this._defaultComparer; - if ((value && value.wrapped) || !comparer(currentValue, entry.effectiveValue)) { - this._onPropertyChanged(property, currentValue, entry.effectiveValue); + let currentValueSource = entry.valueSource; + if (currentValueSource > source) { + return; + } + else if (currentValueSource < source) { + entry.valueSource = source; + } + + if (wrapped || !property.equalityComparer(currentValue, realValue)) { + if (realValue === undefined) { + realValue = this.getEffectiveValue(currentValueSource, entry, property); + } + + entry.effectiveValue = realValue; + this._onPropertyChanged(property, currentValue, realValue); } } - private _defaultComparer(x: any, y: any): boolean { - return x === y; + private getEffectiveValue(currentValueSource: number, entry: PropertyEntry, property: Property): any { + let newValue: any; + switch (currentValueSource) { + case ValueSource.Inherited: + newValue = property.defaultValue; + entry.valueSource = ValueSource.Default; + break; + + case ValueSource.Css: + if (entry.inheritedValue !== undefined) { + newValue = entry.inheritedValue; + entry.valueSource = ValueSource.Inherited; + } + else { + newValue = property.defaultValue; + entry.valueSource = ValueSource.Default; + } + break; + + case ValueSource.Local: + if (entry.cssValue !== undefined) { + newValue = entry.cssValue; + entry.valueSource = ValueSource.Css; + } + else if (entry.inheritedValue !== undefined) { + newValue = entry.inheritedValue; + entry.valueSource = ValueSource.Inherited; + } + else { + newValue = property.defaultValue; + entry.valueSource = ValueSource.Default; + } + break; + + case ValueSource.VisualState: + if (entry.localValue !== undefined) { + newValue = entry.localValue; + entry.valueSource = ValueSource.Local; + } + else if (entry.cssValue !== undefined) { + newValue = entry.cssValue; + entry.valueSource = ValueSource.Css; + } + else if (entry.inheritedValue !== undefined) { + newValue = entry.inheritedValue; + entry.valueSource = ValueSource.Inherited; + } + else { + newValue = property.defaultValue; + entry.valueSource = ValueSource.Default; + } + break; + } + + return newValue; } } \ No newline at end of file diff --git a/tns-core-modules/ui/core/view-common.ts b/tns-core-modules/ui/core/view-common.ts index 73a253269..0ff1d431e 100644 --- a/tns-core-modules/ui/core/view-common.ts +++ b/tns-core-modules/ui/core/view-common.ts @@ -419,7 +419,7 @@ export class View extends ProxyObject implements definition.View { set opacity(value: number) { this.style.opacity = value; } - + //END Style property shortcuts get translateX(): number { @@ -585,16 +585,13 @@ export class View extends ProxyObject implements definition.View { super._onPropertyChanged(property, oldValue, newValue); if (this._childrenCount > 0) { - var shouldUpdateInheritableProps = ((property.metadata && property.metadata.inheritable) && - !(property instanceof styling.Property)); - var that = this; + let shouldUpdateInheritableProps = (property.inheritable && !(property instanceof styling.Property)); if (shouldUpdateInheritableProps) { - var notifyEachChild = function (child: View) { - child._setValue(property, that._getValue(property), ValueSource.Inherited); - return true; - }; this._updatingInheritedProperties = true; - this._eachChildView(notifyEachChild); + this._eachChildView((child) => { + child._setValue(property, this._getValue(property), ValueSource.Inherited); + return true; + }); this._updatingInheritedProperties = false; } } @@ -1010,19 +1007,13 @@ export class View extends ProxyObject implements definition.View { } public _inheritProperties(parentView: View) { - var that = this; - var inheritablePropertySetCallback = function (property: Property) { - if (property instanceof styling.Property) { - return true; - } - if (property.metadata && property.metadata.inheritable) { - var baseValue = parentView._getValue(property); - that._setValue(property, baseValue, ValueSource.Inherited); + parentView._eachSetProperty((property) => { + if (!(property instanceof styling.Property) && property.inheritable) { + let baseValue = parentView._getValue(property); + this._setValue(property, baseValue, ValueSource.Inherited); } return true; - }; - - parentView._eachSetProperty(inheritablePropertySetCallback); + }); } /** @@ -1056,16 +1047,12 @@ export class View extends ProxyObject implements definition.View { ensureBindable(); view._setValue(bindable.Bindable.bindingContextProperty, undefined, ValueSource.Inherited); - var inheritablePropertiesSetCallback = function (property: Property) { - if (property instanceof styling.Property) { - return true; - } - if (property.metadata && property.metadata.inheritable) { + view._eachSetProperty((property) => { + if (!(property instanceof styling.Property) && property.inheritable) { view._resetValue(property, ValueSource.Inherited); } return true; - } - view._eachSetProperty(inheritablePropertiesSetCallback); + }); } public _parentChanged(oldParent: View): void { @@ -1209,7 +1196,7 @@ export class View extends ProxyObject implements definition.View { } } } - + public toString(): string { var str = this.typeName; if (this.id) { diff --git a/tns-core-modules/ui/core/view.android.ts b/tns-core-modules/ui/core/view.android.ts index 65830d7a3..8055476e1 100644 --- a/tns-core-modules/ui/core/view.android.ts +++ b/tns-core-modules/ui/core/view.android.ts @@ -624,7 +624,7 @@ export class ViewStyler implements style.Styler { } private static resetNativeLayoutParamsProperty(view: View, nativeValue: any): void { - ViewStyler.setNativeLayoutParamsProperty(view, style.nativeLayoutParamsProperty.metadata.defaultValue) + ViewStyler.setNativeLayoutParamsProperty(view, style.nativeLayoutParamsProperty.defaultValue) } private static setPaddingProperty(view: View, newValue: Thickness) { diff --git a/tns-core-modules/ui/scroll-view/scroll-view.android.ts b/tns-core-modules/ui/scroll-view/scroll-view.android.ts index c49be54f6..a259afb59 100644 --- a/tns-core-modules/ui/scroll-view/scroll-view.android.ts +++ b/tns-core-modules/ui/scroll-view/scroll-view.android.ts @@ -6,7 +6,7 @@ import enums = require("ui/enums"); global.moduleMerge(common, exports); -common.orientationProperty.metadata.onValueChanged = function scrollViewOrientationChanged(data: dependencyObservable.PropertyChangeData) { +common.orientationProperty.onValueChanged = function scrollViewOrientationChanged(data: dependencyObservable.PropertyChangeData) { (data.object)._onOrientationChanged(data.oldValue, data.newValue); } diff --git a/tns-core-modules/ui/styling/style-property.ts b/tns-core-modules/ui/styling/style-property.ts index 14342e75b..46155592d 100644 --- a/tns-core-modules/ui/styling/style-property.ts +++ b/tns-core-modules/ui/styling/style-property.ts @@ -1,6 +1,6 @@ import definition = require("ui/styling/style-property"); import types = require("utils/types"); -import observable = require("ui/core/dependency-observable"); +import observable = require("ui/core/dependency-observable"); var propertiesByName = {}; var propertiesByCssName = {}; @@ -15,7 +15,7 @@ function registerProperty(property: Property) { propertiesByCssName[property.cssName] = property; propertiesByName[property.name] = property; - if (property.metadata.inheritable) { + if (property.inheritable) { inheritableProperties.push(property); } } @@ -71,63 +71,26 @@ export function getPropertyByCssName(name: string): Property { export function eachProperty(callback: (property: Property) => void) { types.verifyCallback(callback); - - var i; - var key; - var keys = Object.keys(propertiesByName); - - for (i = 0; i < keys.length; i++) { - key = keys[i]; + for (let i = 0, keys = Object.keys(propertiesByName); i < keys.length; i++) { + let key = keys[i]; callback(propertiesByName[key]); } } export function eachInheritableProperty(callback: (property: Property) => void) { types.verifyCallback(callback); - - var i; - for (i = 0; i < inheritableProperties.length; i++) { + for (let i = 0; i < inheritableProperties.length; i++) { callback(inheritableProperties[i]); } } export class Property extends observable.Property implements definition.Property { - private _cssName; - constructor(name: string, cssName: string, metadata: observable.PropertyMetadata, valueConverter?: (value: any) => any) { + constructor(name: string, public cssName: string, metadata: observable.PropertyMetadata, valueConverter?: (value: any) => any) { super(name, "Style", metadata, valueConverter); - this._cssName = cssName; + this.cssName = cssName; registerProperty(this); } - - public get cssName(): string { - return this._cssName; - } - - public _getEffectiveValue(entry: observable.PropertyEntry): any { - if (types.isDefined(entry.visualStateValue)) { - entry.valueSource = observable.ValueSource.VisualState; - return entry.visualStateValue; - } - - if (types.isDefined(entry.localValue)) { - entry.valueSource = observable.ValueSource.Local; - return entry.localValue; - } - - if (types.isDefined(entry.cssValue)) { - entry.valueSource = observable.ValueSource.Css; - return entry.cssValue; - } - - if (types.isDefined(entry.inheritedValue)) { - entry.valueSource = observable.ValueSource.Inherited; - return entry.inheritedValue; - } - - entry.valueSource = observable.ValueSource.Default; - return this.metadata.defaultValue; - } } diff --git a/tns-core-modules/ui/styling/style.ts b/tns-core-modules/ui/styling/style.ts index e5c3bd5b5..d934c4df2 100644 --- a/tns-core-modules/ui/styling/style.ts +++ b/tns-core-modules/ui/styling/style.ts @@ -910,20 +910,17 @@ export class Style extends DependencyObservable implements styling.Style { // The effective value of an inheritable property has changed // propagate the change down to the descendants to update their inherited properties. - if (this._view._childrenCount === 0 || !property.metadata.inheritable) { + if (this._view._childrenCount === 0 || !property.inheritable) { return; } - var eachChild = function (child: View): boolean { + this._view._eachChildView((child: View) => { child.style._inheritStyleProperty(property); return true; - } - - this._view._eachChildView(eachChild); + }); } private _applyStyleProperty(property: Property, newValue: any) { - if (!this._view._shouldApplyStyleHandlers()) { return; } @@ -934,7 +931,7 @@ export class Style extends DependencyObservable implements styling.Style { } try { - var handler: definition.StylePropertyChangedHandler = getHandler(property, this._view); + let handler: definition.StylePropertyChangedHandler = getHandler(property, this._view); if (!handler) { if (trace.enabled) { @@ -946,12 +943,12 @@ export class Style extends DependencyObservable implements styling.Style { trace.write("Found handler for property: " + property.name + ", view:" + this._view, trace.categories.Style); } - var shouldReset = false; - if (property.metadata.equalityComparer) { - shouldReset = property.metadata.equalityComparer(newValue, property.metadata.defaultValue); + let shouldReset = false; + if (property.equalityComparer) { + shouldReset = property.equalityComparer(newValue, property.defaultValue); } else { - shouldReset = (newValue === property.metadata.defaultValue); + shouldReset = (newValue === property.defaultValue); } if (shouldReset) { @@ -971,12 +968,12 @@ export class Style extends DependencyObservable implements styling.Style { } public _inheritStyleProperty(property: Property) { - if (!property.metadata.inheritable) { + if (!property.inheritable) { throw new Error("An attempt was made to inherit a style property which is not marked as 'inheritable'."); } - var currentParent = this._view.parent; - var valueSource: number; + let currentParent = this._view.parent; + let valueSource: number; while (currentParent) { valueSource = currentParent.style._getValueSource(property); @@ -990,9 +987,8 @@ export class Style extends DependencyObservable implements styling.Style { } public _inheritStyleProperties() { - var that = this; styleProperty.eachInheritableProperty((p) => { - that._inheritStyleProperty(p); + this._inheritStyleProperty(p); }); } diff --git a/tns-core-modules/ui/time-picker/time-picker.android.ts b/tns-core-modules/ui/time-picker/time-picker.android.ts index 83dcbb35a..b1abb77ff 100644 --- a/tns-core-modules/ui/time-picker/time-picker.android.ts +++ b/tns-core-modules/ui/time-picker/time-picker.android.ts @@ -34,11 +34,11 @@ export class TimePicker extends common.TimePicker { var c = java.util.Calendar.getInstance(); - if (this.hour === common.TimePicker.hourProperty.metadata.defaultValue) { + if (this.hour === common.TimePicker.hourProperty.defaultValue) { this.hour = c.get(java.util.Calendar.HOUR_OF_DAY); } - if (this.minute === common.TimePicker.minuteProperty.metadata.defaultValue) { + if (this.minute === common.TimePicker.minuteProperty.defaultValue) { this.minute = c.get(java.util.Calendar.MINUTE); }