mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
add profile for functions, profile CSS-es on startup, use __time (#4227)
Call native setters once when a page is instantiated. Fix background not being reset, Label's background is now set to background, not layer Make the nativeView field instead of property for performance reasons
This commit is contained in:
@@ -96,6 +96,7 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
|
||||
public readonly getDefault: symbol;
|
||||
public readonly setNative: symbol;
|
||||
public readonly key: symbol;
|
||||
|
||||
public readonly name: string;
|
||||
public readonly cssName: string;
|
||||
|
||||
@@ -54,92 +54,99 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
||||
public readonly defaultValue: U;
|
||||
public readonly nativeValueChange: (owner: T, value: U) => void;
|
||||
|
||||
public isStyleProperty: boolean;
|
||||
|
||||
public get: () => U;
|
||||
public set: (value: U) => void;
|
||||
public enumerable: boolean = true;
|
||||
public configurable: boolean = true;
|
||||
|
||||
constructor(options: definitions.PropertyOptions<T, U>) {
|
||||
const name = options.name;
|
||||
this.name = name;
|
||||
const propertyName = options.name;
|
||||
this.name = propertyName;
|
||||
|
||||
const key = Symbol(name + ":propertyKey");
|
||||
const key = Symbol(propertyName + ":propertyKey");
|
||||
this.key = key;
|
||||
|
||||
const getDefault: symbol = Symbol(name + ":getDefault");
|
||||
const getDefault: symbol = Symbol(propertyName + ":getDefault");
|
||||
this.getDefault = getDefault;
|
||||
|
||||
const setNative: symbol = Symbol(name + ":setNative");
|
||||
const setNative: symbol = Symbol(propertyName + ":setNative");
|
||||
this.setNative = setNative;
|
||||
|
||||
const defaultValueKey = Symbol(name + ":nativeDefaultValue");
|
||||
const defaultValueKey = Symbol(propertyName + ":nativeDefaultValue");
|
||||
this.defaultValueKey = defaultValueKey;
|
||||
|
||||
const defaultValue: U = options.defaultValue;
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
const eventName = name + "Change";
|
||||
const eventName = propertyName + "Change";
|
||||
const equalityComparer = options.equalityComparer;
|
||||
const affectsLayout: boolean = options.affectsLayout;
|
||||
const valueChanged = options.valueChanged;
|
||||
const valueConverter = options.valueConverter;
|
||||
|
||||
this.set = function (this: T, value: U): void {
|
||||
const reset = value === unsetValue;
|
||||
let unboxedValue: U;
|
||||
const property = this;
|
||||
|
||||
this.set = function (this: T, boxedValue: U): void {
|
||||
const reset = boxedValue === unsetValue;
|
||||
let value: U;
|
||||
let wrapped: boolean;
|
||||
if (reset) {
|
||||
unboxedValue = defaultValue;
|
||||
value = defaultValue;
|
||||
} else {
|
||||
wrapped = value && (<any>value).wrapped;
|
||||
unboxedValue = wrapped ? WrappedValue.unwrap(value) : value;
|
||||
wrapped = boxedValue && (<any>boxedValue).wrapped;
|
||||
value = wrapped ? WrappedValue.unwrap(boxedValue) : boxedValue;
|
||||
|
||||
if (valueConverter && typeof unboxedValue === "string") {
|
||||
unboxedValue = valueConverter(unboxedValue);
|
||||
if (valueConverter && typeof value === "string") {
|
||||
value = valueConverter(value);
|
||||
}
|
||||
}
|
||||
|
||||
const currentValue = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, unboxedValue) : currentValue !== unboxedValue;
|
||||
const oldValue = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
|
||||
|
||||
if (wrapped || changed) {
|
||||
const setNativeValue = this.nativeView && this[setNative];
|
||||
if (reset) {
|
||||
delete this[key];
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, unboxedValue);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
if (setNativeValue) {
|
||||
if (defaultValueKey in this) {
|
||||
this[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
if (this[setNative]) {
|
||||
if (this._suspendNativeUpdatesCount) {
|
||||
if (this._suspendedUpdates) {
|
||||
this._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
this[setNative](defaultValue);
|
||||
if (defaultValueKey in this) {
|
||||
this[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
} else {
|
||||
this[setNative](defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this[key] = unboxedValue;
|
||||
this[key] = value;
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, unboxedValue);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = this[getDefault] ? this[getDefault]() : defaultValue;
|
||||
if (this[setNative]) {
|
||||
if (this._suspendNativeUpdatesCount) {
|
||||
if (this._suspendedUpdates) {
|
||||
this._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = this[getDefault] ? this[getDefault]() : defaultValue;
|
||||
}
|
||||
this[setNative](value);
|
||||
}
|
||||
|
||||
this[setNative](unboxedValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasListeners(eventName)) {
|
||||
this.notify<PropertyChangeData>({
|
||||
eventName: eventName,
|
||||
propertyName: name,
|
||||
object: this,
|
||||
value: unboxedValue,
|
||||
oldValue: currentValue
|
||||
});
|
||||
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
|
||||
if (affectsLayout) {
|
||||
@@ -153,12 +160,12 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
||||
};
|
||||
|
||||
this.nativeValueChange = function (owner: T, value: U): void {
|
||||
const currentValue = key in owner ? owner[key] : defaultValue;
|
||||
const changed = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value;
|
||||
const oldValue = key in owner ? owner[key] : defaultValue;
|
||||
const changed = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
|
||||
if (changed) {
|
||||
owner[key] = value;
|
||||
if (valueChanged) {
|
||||
valueChanged(owner, currentValue, value);
|
||||
valueChanged(owner, oldValue, value);
|
||||
}
|
||||
|
||||
if (owner.nativeView && !(defaultValueKey in owner)) {
|
||||
@@ -166,13 +173,7 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
||||
}
|
||||
|
||||
if (owner.hasListeners(eventName)) {
|
||||
owner.notify<PropertyChangeData>({
|
||||
eventName: eventName,
|
||||
propertyName: name,
|
||||
object: owner,
|
||||
value: value,
|
||||
oldValue: currentValue
|
||||
});
|
||||
owner.notify<PropertyChangeData>({ object: owner, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
|
||||
if (affectsLayout) {
|
||||
@@ -196,6 +197,7 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
||||
return this.key in instance;
|
||||
}
|
||||
}
|
||||
Property.prototype.isStyleProperty = false;
|
||||
|
||||
export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> implements definitions.CoercibleProperty<T, U> {
|
||||
public readonly coerce: (target: T) => void;
|
||||
@@ -203,89 +205,95 @@ export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> imp
|
||||
constructor(options: definitions.CoerciblePropertyOptions<T, U>) {
|
||||
super(options);
|
||||
|
||||
const name = options.name;
|
||||
const propertyName = options.name;
|
||||
const key = this.key;
|
||||
const getDefault: symbol = this.getDefault;
|
||||
const setNative: symbol = this.setNative;
|
||||
const defaultValueKey = this.defaultValueKey;
|
||||
const defaultValue: U = this.defaultValue;
|
||||
|
||||
const coerceKey = Symbol(name + ":coerceKey");
|
||||
const coerceKey = Symbol(propertyName + ":coerceKey");
|
||||
|
||||
const eventName = name + "Change";
|
||||
const eventName = propertyName + "Change";
|
||||
const affectsLayout: boolean = options.affectsLayout;
|
||||
const equalityComparer = options.equalityComparer;
|
||||
const valueChanged = options.valueChanged;
|
||||
const valueConverter = options.valueConverter;
|
||||
const coerceCallback = options.coerceValue;
|
||||
|
||||
const property = this;
|
||||
|
||||
this.coerce = function (target: T): void {
|
||||
const originalValue: U = coerceKey in target ? target[coerceKey] : defaultValue;
|
||||
// need that to make coercing but also fire change events
|
||||
target[name] = originalValue;
|
||||
target[propertyName] = originalValue;
|
||||
}
|
||||
|
||||
this.set = function (this: T, value: U): void {
|
||||
const reset = value === unsetValue;
|
||||
let unboxedValue: U;
|
||||
this.set = function (this: T, boxedValue: U): void {
|
||||
const reset = boxedValue === unsetValue;
|
||||
let value: U;
|
||||
let wrapped: boolean;
|
||||
if (reset) {
|
||||
unboxedValue = defaultValue;
|
||||
value = defaultValue;
|
||||
delete this[coerceKey];
|
||||
} else {
|
||||
wrapped = value && (<any>value).wrapped;
|
||||
unboxedValue = wrapped ? WrappedValue.unwrap(value) : value;
|
||||
wrapped = boxedValue && (<any>boxedValue).wrapped;
|
||||
value = wrapped ? WrappedValue.unwrap(boxedValue) : boxedValue;
|
||||
|
||||
if (valueConverter && typeof unboxedValue === "string") {
|
||||
unboxedValue = valueConverter(unboxedValue);
|
||||
if (valueConverter && typeof value === "string") {
|
||||
value = valueConverter(value);
|
||||
}
|
||||
|
||||
this[coerceKey] = unboxedValue;
|
||||
unboxedValue = coerceCallback(this, unboxedValue);
|
||||
this[coerceKey] = value;
|
||||
value = coerceCallback(this, value);
|
||||
}
|
||||
|
||||
const currentValue = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, unboxedValue) : currentValue !== unboxedValue;
|
||||
const oldValue = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
|
||||
|
||||
if (wrapped || changed) {
|
||||
const setNativeValue = this.nativeView && this[setNative];
|
||||
if (reset) {
|
||||
delete this[key];
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, unboxedValue);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (defaultValueKey in this) {
|
||||
this[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
if (this[setNative]) {
|
||||
if (this._suspendNativeUpdatesCount) {
|
||||
if (this._suspendedUpdates) {
|
||||
this._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
this[setNative](defaultValue);
|
||||
if (defaultValueKey in this) {
|
||||
this[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
} else {
|
||||
this[setNative](defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this[key] = unboxedValue;
|
||||
this[key] = value;
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, unboxedValue);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = this[getDefault] ? this[getDefault]() : defaultValue;
|
||||
if (this[setNative]) {
|
||||
if (this._suspendNativeUpdatesCount) {
|
||||
if (this._suspendedUpdates) {
|
||||
this._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = this[getDefault] ? this[getDefault]() : defaultValue;
|
||||
}
|
||||
this[setNative](value);
|
||||
}
|
||||
|
||||
this[setNative](unboxedValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasListeners(eventName)) {
|
||||
this.notify<PropertyChangeData>({
|
||||
eventName: eventName,
|
||||
propertyName: name,
|
||||
object: this,
|
||||
value: unboxedValue,
|
||||
oldValue: currentValue
|
||||
});
|
||||
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
|
||||
if (affectsLayout) {
|
||||
@@ -378,6 +386,8 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
||||
protected readonly cssValueDescriptor: PropertyDescriptor;
|
||||
protected readonly localValueDescriptor: PropertyDescriptor;
|
||||
|
||||
public isStyleProperty: boolean;
|
||||
|
||||
public readonly key: symbol;
|
||||
public readonly getDefault: symbol;
|
||||
public readonly setNative: symbol;
|
||||
@@ -386,36 +396,38 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
||||
public readonly defaultValue: U;
|
||||
|
||||
constructor(options: definitions.CssPropertyOptions<T, U>) {
|
||||
const name = options.name;
|
||||
this.name = name;
|
||||
const propertyName = options.name;
|
||||
this.name = propertyName;
|
||||
|
||||
this.cssName = `css:${options.cssName}`;
|
||||
this.cssLocalName = options.cssName;
|
||||
|
||||
const key = Symbol(name + ":propertyKey");
|
||||
const key = Symbol(propertyName + ":propertyKey");
|
||||
this.key = key;
|
||||
|
||||
const sourceKey = Symbol(name + ":valueSourceKey");
|
||||
const sourceKey = Symbol(propertyName + ":valueSourceKey");
|
||||
this.sourceKey = sourceKey;
|
||||
|
||||
const getDefault = Symbol(name + ":getDefault");
|
||||
const getDefault = Symbol(propertyName + ":getDefault");
|
||||
this.getDefault = getDefault;
|
||||
|
||||
const setNative = Symbol(name + ":setNative");
|
||||
const setNative = Symbol(propertyName + ":setNative");
|
||||
this.setNative = setNative;
|
||||
|
||||
const defaultValueKey = Symbol(name + ":nativeDefaultValue");
|
||||
const defaultValueKey = Symbol(propertyName + ":nativeDefaultValue");
|
||||
this.defaultValueKey = defaultValueKey;
|
||||
|
||||
const defaultValue: U = options.defaultValue;
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
const eventName = name + "Change";
|
||||
const eventName = propertyName + "Change";
|
||||
const affectsLayout: boolean = options.affectsLayout;
|
||||
const equalityComparer = options.equalityComparer;
|
||||
const valueChanged = options.valueChanged;
|
||||
const valueConverter = options.valueConverter;
|
||||
|
||||
const property = this;
|
||||
|
||||
function setLocalValue(this: T, value: U): void {
|
||||
const reset = value === unsetValue;
|
||||
if (reset) {
|
||||
@@ -429,49 +441,53 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
||||
}
|
||||
}
|
||||
|
||||
const currentValue: U = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value;
|
||||
const oldValue: U = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
|
||||
|
||||
if (changed) {
|
||||
const view = this.view;
|
||||
const setNativeValue = view.nativeView && view[setNative];
|
||||
if (reset) {
|
||||
delete this[key];
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, value);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (defaultValueKey in this) {
|
||||
view[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
view[setNative](defaultValue);
|
||||
if (defaultValueKey in this) {
|
||||
view[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
} else {
|
||||
view[setNative](defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this[key] = value;
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, value);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
view[setNative](value);
|
||||
}
|
||||
|
||||
view[setNative](value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasListeners(eventName)) {
|
||||
this.notify<PropertyChangeData>({
|
||||
eventName: eventName,
|
||||
propertyName: name,
|
||||
object: this,
|
||||
value: value,
|
||||
oldValue: currentValue
|
||||
});
|
||||
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
|
||||
if (affectsLayout) {
|
||||
@@ -499,49 +515,53 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
||||
this[sourceKey] = ValueSource.Css;
|
||||
}
|
||||
|
||||
const currentValue: U = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, value) : currentValue !== value;
|
||||
const oldValue: U = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
|
||||
|
||||
if (changed) {
|
||||
const view = this.view;
|
||||
const setNativeValue = view.nativeView && view[setNative];
|
||||
if (reset) {
|
||||
delete this[key];
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, value);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (defaultValueKey in this) {
|
||||
view[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
view[setNative](defaultValue);
|
||||
if (defaultValueKey in this) {
|
||||
view[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
} else {
|
||||
view[setNative](defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this[key] = value;
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, value);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
view[setNative](value);
|
||||
}
|
||||
|
||||
view[setNative](value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasListeners(eventName)) {
|
||||
this.notify<PropertyChangeData>({
|
||||
eventName: eventName,
|
||||
propertyName: name,
|
||||
object: this,
|
||||
value: value,
|
||||
oldValue: currentValue
|
||||
});
|
||||
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
|
||||
if (affectsLayout) {
|
||||
@@ -587,6 +607,7 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
||||
return this.key in instance;
|
||||
}
|
||||
}
|
||||
CssProperty.prototype.isStyleProperty = true;
|
||||
|
||||
export class CssAnimationProperty<T extends Style, U> {
|
||||
public readonly name: string;
|
||||
@@ -599,10 +620,12 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
|
||||
public readonly keyframe: string;
|
||||
public readonly defaultValueKey: symbol;
|
||||
public readonly computedValueKey: symbol;
|
||||
public readonly key: symbol;
|
||||
|
||||
public readonly defaultValue: U;
|
||||
|
||||
public isStyleProperty: boolean;
|
||||
|
||||
private static properties: { [cssName: string]: CssAnimationProperty<any, any> } = {};
|
||||
|
||||
public _valueConverter?: (value: string) => any;
|
||||
@@ -635,7 +658,7 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
const styleValue = Symbol(propertyName);
|
||||
const keyframeValue = Symbol(keyframeName);
|
||||
const computedValue = Symbol("computed-value:" + propertyName);
|
||||
this.computedValueKey = computedValue;
|
||||
this.key = computedValue;
|
||||
const computedSource = Symbol("computed-source:" + propertyName);
|
||||
|
||||
this.getDefault = Symbol(propertyName + ":getDefault");
|
||||
@@ -643,13 +666,15 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
const setNative = this.setNative = Symbol(propertyName + ":setNative");
|
||||
const eventName = propertyName + "Change";
|
||||
|
||||
const property = this;
|
||||
|
||||
function descriptor(symbol: symbol, propertySource: ValueSource, enumerable: boolean, configurable: boolean, getsComputed: boolean): PropertyDescriptor {
|
||||
return {
|
||||
enumerable, configurable,
|
||||
get: getsComputed ? function (this: T) { return this[computedValue]; } : function (this: T) { return this[symbol]; },
|
||||
set(this: T, value: U) {
|
||||
let prev = this[computedValue];
|
||||
if (value === unsetValue) {
|
||||
set(this: T, boxedValue: U) {
|
||||
let oldValue = this[computedValue];
|
||||
if (boxedValue === unsetValue) {
|
||||
this[symbol] = unsetValue;
|
||||
if (this[computedSource] === propertySource) {
|
||||
// Fallback to lower value source.
|
||||
@@ -665,30 +690,37 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (valueConverter && typeof value === "string") {
|
||||
value = valueConverter(value);
|
||||
if (valueConverter && typeof boxedValue === "string") {
|
||||
boxedValue = valueConverter(boxedValue);
|
||||
}
|
||||
this[symbol] = value;
|
||||
this[symbol] = boxedValue;
|
||||
if (this[computedSource] <= propertySource) {
|
||||
this[computedSource] = propertySource;
|
||||
this[computedValue] = value;
|
||||
this[computedValue] = boxedValue;
|
||||
}
|
||||
}
|
||||
let next = this[computedValue];
|
||||
if (prev !== next && (!equalityComparer || !equalityComparer(prev, next))) {
|
||||
let value = this[computedValue];
|
||||
if (oldValue !== value && (!equalityComparer || !equalityComparer(oldValue, value))) {
|
||||
if (valueChanged) {
|
||||
valueChanged(this, prev, next);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
const view = this.view;
|
||||
if (view.nativeView && view[setNative]) {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
|
||||
view[setNative](next);
|
||||
const view = this.view;
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
|
||||
view[setNative](value);
|
||||
}
|
||||
}
|
||||
if (this.hasListeners(eventName)) {
|
||||
this.notify<PropertyChangeData>({ eventName, object: this, propertyName, value, oldValue: prev });
|
||||
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -726,16 +758,17 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
}
|
||||
|
||||
public isSet(instance: T): boolean {
|
||||
return instance[this.computedValueKey] !== unsetValue;
|
||||
return instance[this.key] !== unsetValue;
|
||||
}
|
||||
}
|
||||
CssAnimationProperty.prototype.isStyleProperty = true;
|
||||
|
||||
export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U> implements definitions.InheritedCssProperty<T, U> {
|
||||
public setInheritedValue: (value: U) => void;
|
||||
|
||||
constructor(options: definitions.CssPropertyOptions<T, U>) {
|
||||
super(options);
|
||||
const name = options.name;
|
||||
const propertyName = options.name;
|
||||
|
||||
const key = this.key;
|
||||
const sourceKey = this.sourceKey;
|
||||
@@ -743,15 +776,17 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
|
||||
const setNative = this.setNative;
|
||||
const defaultValueKey = this.defaultValueKey;
|
||||
|
||||
const eventName = name + "Change";
|
||||
const eventName = propertyName + "Change";
|
||||
const defaultValue: U = options.defaultValue;
|
||||
const affectsLayout: boolean = options.affectsLayout;
|
||||
const equalityComparer = options.equalityComparer;
|
||||
const valueChanged = options.valueChanged;
|
||||
const valueConverter = options.valueConverter;
|
||||
|
||||
const setFunc = (valueSource: ValueSource) => function (this: T, value: any): void {
|
||||
const reset = value === unsetValue;
|
||||
const property = this;
|
||||
|
||||
const setFunc = (valueSource: ValueSource) => function (this: T, boxedValue: any): void {
|
||||
const reset = boxedValue === unsetValue;
|
||||
const currentValueSource: number = this[sourceKey] || ValueSource.Default;
|
||||
if (reset) {
|
||||
// If we want to reset cssValue and we have localValue - return;
|
||||
@@ -765,71 +800,76 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
|
||||
}
|
||||
|
||||
const view = this.view;
|
||||
let newValue: U;
|
||||
let value: U;
|
||||
if (reset) {
|
||||
// If unsetValue - we want to reset this property.
|
||||
let parent = view.parent;
|
||||
let style = parent ? parent.style : null;
|
||||
// If we have parent and it has non-default value we use as our inherited value.
|
||||
if (style && style[sourceKey] > ValueSource.Default) {
|
||||
newValue = style[name];
|
||||
value = style[propertyName];
|
||||
this[sourceKey] = ValueSource.Inherited;
|
||||
} else {
|
||||
newValue = defaultValue;
|
||||
value = defaultValue;
|
||||
delete this[sourceKey];
|
||||
}
|
||||
} else {
|
||||
this[sourceKey] = valueSource;
|
||||
if (valueConverter && typeof value === "string") {
|
||||
newValue = valueConverter(value);
|
||||
if (valueConverter && typeof boxedValue === "string") {
|
||||
value = valueConverter(boxedValue);
|
||||
} else {
|
||||
newValue = value;
|
||||
value = boxedValue;
|
||||
}
|
||||
}
|
||||
|
||||
const currentValue: U = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(currentValue, newValue) : currentValue !== newValue;
|
||||
const oldValue: U = key in this ? this[key] : defaultValue;
|
||||
const changed: boolean = equalityComparer ? !equalityComparer(oldValue, value) : oldValue !== value;
|
||||
|
||||
if (changed) {
|
||||
const view = this.view;
|
||||
const setNativeValue = view.nativeView && view[setNative];
|
||||
if (reset) {
|
||||
delete this[key];
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, newValue);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (defaultValueKey in this) {
|
||||
view[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
view[setNative](defaultValue);
|
||||
if (defaultValueKey in this) {
|
||||
view[setNative](this[defaultValueKey]);
|
||||
delete this[defaultValueKey];
|
||||
} else {
|
||||
view[setNative](defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this[key] = newValue;
|
||||
this[key] = value;
|
||||
if (valueChanged) {
|
||||
valueChanged(this, currentValue, newValue);
|
||||
valueChanged(this, oldValue, value);
|
||||
}
|
||||
|
||||
if (setNativeValue) {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
if (view[setNative]) {
|
||||
if (view._suspendNativeUpdatesCount) {
|
||||
if (view._suspendedUpdates) {
|
||||
view._suspendedUpdates[propertyName] = property;
|
||||
}
|
||||
} else {
|
||||
if (!(defaultValueKey in this)) {
|
||||
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
|
||||
view[setNative](newValue);
|
||||
view[setNative](value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasListeners(eventName)) {
|
||||
this.notify<PropertyChangeData>({
|
||||
eventName: eventName,
|
||||
propertyName: name,
|
||||
object: this,
|
||||
value: newValue,
|
||||
oldValue: currentValue
|
||||
});
|
||||
this.notify<PropertyChangeData>({ object: this, eventName, propertyName, value, oldValue });
|
||||
}
|
||||
|
||||
if (affectsLayout) {
|
||||
@@ -845,7 +885,7 @@ export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U>
|
||||
}
|
||||
} else {
|
||||
if (childValueSource <= ValueSource.Inherited) {
|
||||
setInheritedFunc.call(childStyle, newValue);
|
||||
setInheritedFunc.call(childStyle, value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -891,18 +931,22 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
|
||||
function setLocalValue(this: T, value: string | P): void {
|
||||
if (this[key] !== value) {
|
||||
this[key] = value;
|
||||
for (let [p, v] of converter(value)) {
|
||||
this[p.name] = v;
|
||||
}
|
||||
this.view._batchUpdate(() => {
|
||||
for (let [p, v] of converter(value)) {
|
||||
this[p.name] = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setCssValue(this: T, value: string): void {
|
||||
if (this[key] !== value) {
|
||||
this[key] = value;
|
||||
for (let [p, v] of converter(value)) {
|
||||
this[p.cssName] = v;
|
||||
}
|
||||
this.view._batchUpdate(() => {
|
||||
for (let [p, v] of converter(value)) {
|
||||
this[p.cssName] = v;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,7 +987,7 @@ function inheritablePropertyValuesOn(view: ViewBase): Array<{ property: Inherite
|
||||
const valueSource: number = view[sourceKey] || ValueSource.Default;
|
||||
if (valueSource !== ValueSource.Default) {
|
||||
// use prop.name as it will return value or default value.
|
||||
// prop.key will return undefined if property is set t the same value as default one.
|
||||
// prop.key will return undefined if property is set the same value as default one.
|
||||
array.push({ property: prop, value: view[prop.name] });
|
||||
}
|
||||
}
|
||||
@@ -958,7 +1002,7 @@ function inheritableCssPropertyValuesOn(style: Style): Array<{ property: Inherit
|
||||
const valueSource: number = style[sourceKey] || ValueSource.Default;
|
||||
if (valueSource !== ValueSource.Default) {
|
||||
// use prop.name as it will return value or default value.
|
||||
// prop.key will return undefined if property is set t the same value as default one.
|
||||
// prop.key will return undefined if property is set the same value as default one.
|
||||
array.push({ property: prop, value: style[prop.name] });
|
||||
}
|
||||
}
|
||||
@@ -966,7 +1010,54 @@ function inheritableCssPropertyValuesOn(style: Style): Array<{ property: Inherit
|
||||
return array;
|
||||
}
|
||||
|
||||
type PropertyInterface = Property<ViewBase, any> | CssProperty<Style, any> | CssAnimationProperty<Style, any>;
|
||||
|
||||
export const initNativeView = profile('"properties".initNativeView', function initNativeView(view: ViewBase): void {
|
||||
if (view._suspendedUpdates) {
|
||||
applyPendingNativeSetters(view);
|
||||
} else {
|
||||
applyAllNativeSetters(view);
|
||||
}
|
||||
// Would it be faster to delete all members of the old object?
|
||||
view._suspendedUpdates = {};
|
||||
});
|
||||
|
||||
export function applyPendingNativeSetters(view: ViewBase): void {
|
||||
// TODO: Check what happens if a view was suspended and its value was reset, or set back to default!
|
||||
const suspendedUpdates = view._suspendedUpdates;
|
||||
for (var propertyName in suspendedUpdates) {
|
||||
const property = <PropertyInterface>suspendedUpdates[propertyName];
|
||||
const setNative = property.setNative;
|
||||
if (view[setNative]) {
|
||||
const { getDefault, isStyleProperty, defaultValueKey, defaultValue } = property;
|
||||
let value;
|
||||
if (isStyleProperty) {
|
||||
const style = view.style;
|
||||
if ((<CssProperty<Style, any> | CssAnimationProperty<Style, any>>property).isSet(view.style)) {
|
||||
if (!(defaultValueKey in style)) {
|
||||
style[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
value = view.style[propertyName];
|
||||
} else {
|
||||
value = style[defaultValueKey];
|
||||
}
|
||||
} else {
|
||||
if ((<Property<ViewBase, any>>property).isSet(view)) {
|
||||
if (!(defaultValueKey in view)) {
|
||||
view[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
|
||||
}
|
||||
value = view[propertyName];
|
||||
} else {
|
||||
value = view[defaultValueKey];
|
||||
}
|
||||
}
|
||||
// TODO: Only if value is different from the value before the scope was created.
|
||||
view[setNative](value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function applyAllNativeSetters(view: ViewBase): void {
|
||||
let symbols = Object.getOwnPropertySymbols(view);
|
||||
for (let symbol of symbols) {
|
||||
const property: Property<any, any> = symbolPropertyMap[symbol];
|
||||
@@ -1007,7 +1098,7 @@ export const initNativeView = profile('"properties".initNativeView', function in
|
||||
view[property.setNative](value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function resetNativeView(view: ViewBase): void {
|
||||
let symbols = Object.getOwnPropertySymbols(view);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @module "ui/core/view-base"
|
||||
*/ /** */
|
||||
|
||||
import { Property, InheritedProperty, Style } from "../properties";
|
||||
import { Property, CssProperty, CssAnimationProperty, InheritedProperty, Style } from "../properties";
|
||||
import { BindingOptions, Observable } from "../bindable";
|
||||
|
||||
import { SelectorCore } from "../../styling/css-selector";
|
||||
@@ -97,6 +97,15 @@ export abstract class ViewBase extends Observable {
|
||||
* @private
|
||||
*/
|
||||
_defaultPaddingLeft: number;
|
||||
|
||||
/**
|
||||
* A property bag holding suspended native updates.
|
||||
* Native setters that had to execute while there was no native view,
|
||||
* or the view was detached from the visual tree etc. will accumulate in this object,
|
||||
* and will be applied when all prerequisites are met.
|
||||
* @private
|
||||
*/
|
||||
_suspendedUpdates: { [propertyName: string]: Property<ViewBase, any> | CssProperty<Style, any> | CssAnimationProperty<Style, any> };
|
||||
//@endprivate
|
||||
|
||||
public effectiveMinWidth: number;
|
||||
@@ -128,7 +137,12 @@ export abstract class ViewBase extends Observable {
|
||||
|
||||
public ios: any;
|
||||
public android: any;
|
||||
|
||||
/**
|
||||
* read-only. If you want to set out-of-band the nativeView use the setNativeView method.
|
||||
*/
|
||||
public nativeView: any;
|
||||
|
||||
public bindingContext: any;
|
||||
public recycleNativeView: boolean;
|
||||
|
||||
@@ -181,6 +195,7 @@ export abstract class ViewBase extends Observable {
|
||||
|
||||
public onLoaded(): void;
|
||||
public onUnloaded(): void;
|
||||
public onResumeNativeUpdates(): void;
|
||||
|
||||
public bind(options: BindingOptions, source?: Object): void;
|
||||
public unbind(property: string): void;
|
||||
@@ -251,6 +266,13 @@ export abstract class ViewBase extends Observable {
|
||||
*/
|
||||
resetNativeView(): void;
|
||||
|
||||
/**
|
||||
* Set the nativeView field performing extra checks and updates to the native properties on the new view.
|
||||
* Use in cases where the createNativeView is not suitable.
|
||||
* As an example use in item controls where the native parent view will create the native views for child items.
|
||||
*/
|
||||
setNativeView(view: any): void;
|
||||
|
||||
_isAddedToNativeVisualTree: boolean;
|
||||
|
||||
/**
|
||||
@@ -278,12 +300,13 @@ export abstract class ViewBase extends Observable {
|
||||
public _styleScope: any;
|
||||
|
||||
/**
|
||||
* Determines the depth of batchUpdates.
|
||||
* When the value is 0 the current updates are not batched.
|
||||
* If the value is 1 or greater, the current updates are batched.
|
||||
* Do not set this field, the _batchUpdate method is responsible to keep the count up to date.
|
||||
* Determines the depth of suspended updates.
|
||||
* When the value is 0 the current property updates are not batched nor scoped and must be immediately applied.
|
||||
* If the value is 1 or greater, the current updates are batched and does not have to provide immediate update.
|
||||
* Do not set this field, the _batchUpdate method is responsible to keep the count up to date,
|
||||
* as well as adding/rmoving the view to/from the visual tree.
|
||||
*/
|
||||
public _batchUpdateScope: number;
|
||||
public _suspendNativeUpdatesCount: number;
|
||||
|
||||
/**
|
||||
* Allow multiple updates to be performed on the instance at once.
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../la
|
||||
import { KeyframeAnimation } from "../../animation/keyframe-animation";
|
||||
|
||||
// Types.
|
||||
import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, resetCSSProperties, initNativeView, resetNativeView } from "../properties";
|
||||
import { Property, CssProperty, CssAnimationProperty, InheritedProperty, Style, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, resetCSSProperties, initNativeView, resetNativeView } from "../properties";
|
||||
import { Binding, BindingOptions, Observable, WrappedValue, PropertyChangeData, traceEnabled, traceWrite, traceCategories, traceNotifyEvent } from "../bindable";
|
||||
import { isIOS, isAndroid } from "../../../platform";
|
||||
import { layout } from "../../../utils/utils";
|
||||
@@ -141,6 +141,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
private _registeredAnimations: Array<KeyframeAnimation>;
|
||||
private _visualState: string;
|
||||
private _inlineStyleSelector: SelectorCore;
|
||||
private __nativeView: any;
|
||||
|
||||
public bindingContext: any;
|
||||
public nativeView: any;
|
||||
@@ -155,6 +156,8 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
public _isAddedToNativeVisualTree: boolean;
|
||||
public _cssState: ssm.CssState;
|
||||
public _styleScope: ssm.StyleScope;
|
||||
public _suspendedUpdates: { [propertyName: string]: Property<ViewBase, any> | CssProperty<Style, any> | CssAnimationProperty<Style, any> };
|
||||
public _suspendNativeUpdatesCount: number;
|
||||
|
||||
// Dynamic properties.
|
||||
left: Length;
|
||||
@@ -265,6 +268,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
@profile
|
||||
public onLoaded() {
|
||||
this._isLoaded = true;
|
||||
this._resumeNativeUpdates();
|
||||
this._loadEachChild();
|
||||
this._emit("loaded");
|
||||
}
|
||||
@@ -278,18 +282,27 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
|
||||
@profile
|
||||
public onUnloaded() {
|
||||
this._suspendNativeUpdates();
|
||||
this._unloadEachChild();
|
||||
this._isLoaded = false;
|
||||
this._emit("unloaded");
|
||||
}
|
||||
|
||||
public _batchUpdateScope: number;
|
||||
public _suspendNativeUpdates(): void {
|
||||
this._suspendNativeUpdatesCount++;
|
||||
}
|
||||
public _resumeNativeUpdates(): void {
|
||||
this._suspendNativeUpdatesCount--;
|
||||
if (!this._suspendNativeUpdatesCount) {
|
||||
this.onResumeNativeUpdates();
|
||||
}
|
||||
}
|
||||
public _batchUpdate<T>(callback: () => T): T {
|
||||
try {
|
||||
++this._batchUpdateScope;
|
||||
this._suspendNativeUpdates();
|
||||
return callback();
|
||||
} finally {
|
||||
--this._batchUpdateScope;
|
||||
this._resumeNativeUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,6 +651,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
|
||||
@profile
|
||||
public _setupUI(context: android.content.Context, atIndex?: number, parentIsLoaded?: boolean) {
|
||||
|
||||
traceNotifyEvent(this, "_setupUI");
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`${this}._setupUI(${context})`, traceCategories.VisualTreeEvents);
|
||||
@@ -650,9 +664,8 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
this._context = context;
|
||||
traceNotifyEvent(this, "_onContextChanged");
|
||||
|
||||
let currentNativeView = this.nativeView;
|
||||
let nativeView;
|
||||
if (isAndroid) {
|
||||
let nativeView: android.view.View;
|
||||
if (this.recycleNativeView) {
|
||||
nativeView = <android.view.View>getNativeView(context, this.typeName);
|
||||
}
|
||||
@@ -661,7 +674,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
nativeView = <android.view.View>this.createNativeView();
|
||||
}
|
||||
|
||||
this._androidView = this.nativeView = nativeView;
|
||||
this._androidView = nativeView;
|
||||
if (nativeView) {
|
||||
let result: android.graphics.Rect = (<any>nativeView).defaultPaddings;
|
||||
if (result === undefined) {
|
||||
@@ -690,24 +703,22 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
}
|
||||
} else {
|
||||
// TODO: Implement _createNativeView for iOS
|
||||
const nativeView = this.createNativeView();
|
||||
if (!currentNativeView && nativeView) {
|
||||
this.nativeView = this._iosView = nativeView;
|
||||
nativeView = this.createNativeView();
|
||||
if (nativeView) {
|
||||
this._iosView = nativeView;
|
||||
}
|
||||
}
|
||||
|
||||
this.initNativeView();
|
||||
// This will account for nativeView that is created in createNativeView, recycled
|
||||
// or for backward compatability - set before _setupUI in iOS contructor.
|
||||
this.setNativeView(nativeView || this.nativeView);
|
||||
|
||||
if (this.parent) {
|
||||
let nativeIndex = this.parent._childIndexToNativeChildIndex(atIndex);
|
||||
this._isAddedToNativeVisualTree = this.parent._addViewToNativeVisualTree(this, nativeIndex);
|
||||
}
|
||||
|
||||
if (this.nativeView) {
|
||||
if (currentNativeView !== this.nativeView) {
|
||||
initNativeView(this);
|
||||
}
|
||||
}
|
||||
this._resumeNativeUpdates();
|
||||
|
||||
this.eachChild((child) => {
|
||||
child._setupUI(context);
|
||||
@@ -715,6 +726,23 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
});
|
||||
}
|
||||
|
||||
setNativeView(value: any): void {
|
||||
if (this.__nativeView === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.__nativeView) {
|
||||
this._suspendNativeUpdates();
|
||||
// We may do a `this.resetNativeView()` here?
|
||||
}
|
||||
this.__nativeView = this.nativeView = value;
|
||||
if (this.__nativeView) {
|
||||
this._suspendedUpdates = undefined;
|
||||
this.initNativeView();
|
||||
this._resumeNativeUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public _tearDownUI(force?: boolean) {
|
||||
// No context means we are already teared down.
|
||||
@@ -747,8 +775,10 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
|
||||
this.disposeNativeView();
|
||||
|
||||
this._suspendNativeUpdates();
|
||||
|
||||
if (isAndroid) {
|
||||
this.nativeView = null;
|
||||
this.setNativeView(null);
|
||||
this._androidView = null;
|
||||
}
|
||||
|
||||
@@ -813,6 +843,8 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
}
|
||||
|
||||
public _parentChanged(oldParent: ViewBase): void {
|
||||
const newParent = this.parent;
|
||||
|
||||
//Overridden
|
||||
if (oldParent) {
|
||||
clearInheritedProperties(this);
|
||||
@@ -820,12 +852,16 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
oldParent.off("bindingContextChange", this.bindingContextChanged, this);
|
||||
}
|
||||
} else if (this.shouldAddHandlerToParentBindingContextChanged) {
|
||||
const parent = this.parent;
|
||||
parent.on("bindingContextChange", this.bindingContextChanged, this);
|
||||
this.bindings.get("bindingContext").bind(parent.bindingContext);
|
||||
newParent.on("bindingContextChange", this.bindingContextChanged, this);
|
||||
this.bindings.get("bindingContext").bind(newParent.bindingContext);
|
||||
}
|
||||
}
|
||||
|
||||
public onResumeNativeUpdates(): void {
|
||||
// Apply native setters...
|
||||
initNativeView(this);
|
||||
}
|
||||
|
||||
public _registerAnimation(animation: KeyframeAnimation) {
|
||||
if (this._registeredAnimations === undefined) {
|
||||
this._registeredAnimations = new Array<KeyframeAnimation>();
|
||||
@@ -880,7 +916,11 @@ ViewBase.prototype._defaultPaddingBottom = 0;
|
||||
ViewBase.prototype._defaultPaddingLeft = 0;
|
||||
ViewBase.prototype._isViewBase = true;
|
||||
|
||||
ViewBase.prototype._batchUpdateScope = 0;
|
||||
// Removing from visual tree does +1, adding to visual tree does -1, see parentChanged
|
||||
// Removing the nativeView does +1, adding the nativeView does -1, see set nativeView
|
||||
// Pre _setupUI and post _tearDownUI does +1, in between _setupUI and _tearDownUI is -1
|
||||
// Initially launch with 2, native updates wont fire unless both added to the JS visual tree and got nativeView.
|
||||
ViewBase.prototype._suspendNativeUpdatesCount = 3;
|
||||
|
||||
export const bindingContextProperty = new InheritedProperty<ViewBase, any>({ name: "bindingContext" });
|
||||
bindingContextProperty.register(ViewBase);
|
||||
|
||||
@@ -770,6 +770,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
public _setNativeClipToBounds() {
|
||||
//
|
||||
}
|
||||
|
||||
public _redrawNativeBackground(value: any): void {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
export const automationTextProperty = new Property<ViewCommon, string>({ name: "automationText" });
|
||||
|
||||
@@ -429,11 +429,7 @@ export class View extends ViewCommon {
|
||||
return this.nativeView.getBackground();
|
||||
}
|
||||
[backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) {
|
||||
if (value instanceof android.graphics.drawable.Drawable) {
|
||||
this.nativeView.setBackground(value);
|
||||
} else {
|
||||
androidBackground.onBackgroundOrBorderPropertyChanged(this);
|
||||
}
|
||||
this._redrawNativeBackground(value);
|
||||
}
|
||||
|
||||
[minWidthProperty.setNative](value: Length) {
|
||||
@@ -451,6 +447,14 @@ export class View extends ViewCommon {
|
||||
this._setMinHeightNative(this.minHeight);
|
||||
}
|
||||
}
|
||||
|
||||
_redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void {
|
||||
if (value instanceof android.graphics.drawable.Drawable) {
|
||||
this.nativeView.setBackground(value);
|
||||
} else {
|
||||
androidBackground.onBackgroundOrBorderPropertyChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomLayoutView extends View implements CustomLayoutViewDefinition {
|
||||
|
||||
4
tns-core-modules/ui/core/view/view.d.ts
vendored
4
tns-core-modules/ui/core/view/view.d.ts
vendored
@@ -554,6 +554,10 @@ export abstract class View extends ViewBase implements ApplyXmlAttributes {
|
||||
* @private
|
||||
*/
|
||||
_setMinHeightNative(value: Length): void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_redrawNativeBackground(value: any): void;
|
||||
//@endprivate
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,13 @@ export class View extends ViewCommon {
|
||||
private _privateFlags: number = PFLAG_LAYOUT_REQUIRED | PFLAG_FORCE_LAYOUT;
|
||||
private _cachedFrame: CGRect;
|
||||
private _suspendCATransaction = false;
|
||||
/**
|
||||
* Native background states.
|
||||
* - `unset` - is the default, from this state it transitions to "invalid" in the base backgroundInternalProperty.setNative, overriding it without calling `super` will prevent the background from ever being drawn.
|
||||
* - `invalid` - the view background must be redrawn on the next layot.
|
||||
* - `drawn` - the view background has been property drawn, on subsequent layouts it may need to be redrawn if the background depends on the view's size.
|
||||
*/
|
||||
_nativeBackgroundState: "unset" | "invalid" | "drawn";
|
||||
|
||||
// get nativeView(): UIView {
|
||||
// return this.ios;
|
||||
@@ -91,6 +98,9 @@ export class View extends ViewCommon {
|
||||
|
||||
if (sizeChanged) {
|
||||
this._onSizeChanged();
|
||||
} else if (this._nativeBackgroundState === "invalid") {
|
||||
let background = this.style.backgroundInternal;
|
||||
this._redrawNativeBackground(background);
|
||||
}
|
||||
|
||||
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
|
||||
@@ -210,7 +220,7 @@ export class View extends ViewCommon {
|
||||
|
||||
let myPointInWindow = this.nativeView.convertPointToView(this.nativeView.bounds.origin, null);
|
||||
let otherPointInWindow = otherView.nativeView.convertPointToView(otherView.nativeView.bounds.origin, null);
|
||||
return {
|
||||
return {
|
||||
x: myPointInWindow.x - otherPointInWindow.x,
|
||||
y: myPointInWindow.y - otherPointInWindow.y
|
||||
};
|
||||
@@ -223,8 +233,10 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
let background = this.style.backgroundInternal;
|
||||
if (!background.isEmpty() && this[backgroundInternalProperty.setNative]) {
|
||||
this[backgroundInternalProperty.setNative](background);
|
||||
const backgroundDependsOnSize = background.image || !background.hasUniformBorder();
|
||||
|
||||
if (this._nativeBackgroundState === "invalid" || (this._nativeBackgroundState === "drawn" && backgroundDependsOnSize)) {
|
||||
this._redrawNativeBackground(background);
|
||||
}
|
||||
|
||||
let clipPath = this.style.clipPath;
|
||||
@@ -274,7 +286,7 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
public _isPresentationLayerUpdateSuspeneded() {
|
||||
return this._suspendCATransaction || this._batchUpdateScope;
|
||||
return this._suspendCATransaction || this._suspendNativeUpdatesCount;
|
||||
}
|
||||
|
||||
[isEnabledProperty.getDefault](): boolean {
|
||||
@@ -395,6 +407,13 @@ export class View extends ViewCommon {
|
||||
return this.nativeView.backgroundColor;
|
||||
}
|
||||
[backgroundInternalProperty.setNative](value: UIColor | Background) {
|
||||
this._nativeBackgroundState = "invalid";
|
||||
if (this.isLayoutValid) {
|
||||
this._redrawNativeBackground(value);
|
||||
}
|
||||
}
|
||||
|
||||
_redrawNativeBackground(value: UIColor | Background): void {
|
||||
let updateSuspended = this._isPresentationLayerUpdateSuspeneded();
|
||||
if (!updateSuspended) {
|
||||
CATransaction.begin();
|
||||
@@ -412,6 +431,8 @@ export class View extends ViewCommon {
|
||||
if (!updateSuspended) {
|
||||
CATransaction.commit();
|
||||
}
|
||||
|
||||
this._nativeBackgroundState = "drawn";
|
||||
}
|
||||
|
||||
_setNativeClipToBounds() {
|
||||
@@ -419,6 +440,7 @@ export class View extends ViewCommon {
|
||||
this.nativeView.clipsToBounds = backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderRadius();
|
||||
}
|
||||
}
|
||||
View.prototype._nativeBackgroundState = "unset";
|
||||
|
||||
export class CustomLayoutView extends View {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Label as LabelDefinition } from ".";
|
||||
import { Background } from "../styling/background";
|
||||
import {
|
||||
TextBase, View, layout, backgroundInternalProperty,
|
||||
TextBase, View, layout,
|
||||
borderTopWidthProperty, borderRightWidthProperty, borderBottomWidthProperty, borderLeftWidthProperty,
|
||||
paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty, whiteSpaceProperty,
|
||||
Length, WhiteSpace
|
||||
@@ -95,10 +95,7 @@ export class Label extends TextBase implements LabelDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
[backgroundInternalProperty.getDefault](): any /* CGColor */ {
|
||||
return this.nativeView.layer.backgroundColor;
|
||||
}
|
||||
[backgroundInternalProperty.setNative](value: Background) {
|
||||
_redrawNativeBackground(value: UIColor | Background): void {
|
||||
if (value instanceof Background) {
|
||||
ios.createBackgroundUIColor(this, (color: UIColor) => {
|
||||
const cgColor = color ? color.CGColor : null;
|
||||
|
||||
@@ -15,8 +15,6 @@ export class ListPicker extends ListPickerBase {
|
||||
this.nativeView = this._ios = UIPickerView.new();
|
||||
this._ios.dataSource = this._dataSource = ListPickerDataSource.initWithOwner(new WeakRef(this));
|
||||
this._delegate = ListPickerDelegateImpl.initWithOwner(new WeakRef(this));
|
||||
|
||||
this.nativeView = this._ios;
|
||||
}
|
||||
|
||||
@profile
|
||||
|
||||
@@ -285,8 +285,10 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
|
||||
private _resetCssValues() {
|
||||
const resetCssValuesFunc = (view: View): boolean => {
|
||||
view._cancelAllAnimations();
|
||||
resetCSSProperties(view.style);
|
||||
view._batchUpdate(() => {
|
||||
view._cancelAllAnimations();
|
||||
resetCSSProperties(view.style);
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.nativeView = undefined;
|
||||
this.nativeView = undefined;
|
||||
}
|
||||
|
||||
// No native view for proxy container.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Font } from "../styling/font";
|
||||
import {
|
||||
SegmentedBarItemBase, SegmentedBarBase, selectedIndexProperty, itemsProperty, selectedBackgroundColorProperty,
|
||||
colorProperty, fontInternalProperty, fontSizeProperty, Color, initNativeView, layout
|
||||
colorProperty, fontInternalProperty, fontSizeProperty, Color, layout
|
||||
} from "./segmented-bar-common";
|
||||
|
||||
export * from "./segmented-bar-common";
|
||||
@@ -92,18 +92,13 @@ function initializeNativeClasses(): void {
|
||||
export class SegmentedBarItem extends SegmentedBarItemBase {
|
||||
nativeView: android.widget.TextView;
|
||||
|
||||
public createNativeView(): android.widget.TextView {
|
||||
return this.nativeView;
|
||||
}
|
||||
|
||||
public setupNativeView(tabIndex: number): void {
|
||||
// TabHost.TabSpec.setIndicator DOES NOT WORK once the title has been set.
|
||||
// http://stackoverflow.com/questions/2935781/modify-tab-indicator-dynamically-in-android
|
||||
const titleTextView = <android.widget.TextView>this.parent.nativeView.getTabWidget().getChildAt(tabIndex).findViewById(TITLE_TEXT_VIEW_ID);
|
||||
|
||||
this.nativeView = titleTextView;
|
||||
this.setNativeView(titleTextView);
|
||||
if (titleTextView) {
|
||||
initNativeView(this);
|
||||
if (this.titleDirty) {
|
||||
this._update();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
tabTextColorProperty, tabBackgroundColorProperty, selectedTabTextColorProperty,
|
||||
androidSelectedTabHighlightColorProperty, androidOffscreenTabLimitProperty,
|
||||
fontSizeProperty, fontInternalProperty, View, layout,
|
||||
traceCategory, traceEnabled, traceWrite, initNativeView, Color
|
||||
traceCategory, traceEnabled, traceWrite, Color
|
||||
} from "./tab-view-common"
|
||||
import { textTransformProperty, TextTransform, getTransformedText } from "../text-base";
|
||||
import { fromFileOrResource } from "../../image-source";
|
||||
@@ -212,13 +212,6 @@ export class TabViewItem extends TabViewItemBase {
|
||||
return this.nativeView;
|
||||
}
|
||||
|
||||
public setNativeView(textView: android.widget.TextView): void {
|
||||
this.nativeView = textView;
|
||||
if (textView) {
|
||||
initNativeView(this);
|
||||
}
|
||||
}
|
||||
|
||||
public _update(): void {
|
||||
const tv = this.nativeView;
|
||||
if (tv) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {
|
||||
TabViewBase, TabViewItemBase, itemsProperty, selectedIndexProperty,
|
||||
tabTextColorProperty, tabBackgroundColorProperty, selectedTabTextColorProperty, iosIconRenderingModeProperty,
|
||||
View, fontInternalProperty, layout, traceEnabled, traceWrite, traceCategories, Color, initNativeView
|
||||
View, fontInternalProperty, layout, traceEnabled, traceWrite, traceCategories, Color
|
||||
} from "./tab-view-common"
|
||||
import { textTransformProperty, TextTransform, getTransformedText } from "../text-base";
|
||||
import { fromFileOrResource } from "../../image-source";
|
||||
@@ -128,13 +128,12 @@ export class TabViewItem extends TabViewItemBase {
|
||||
|
||||
public setViewController(controller: UIViewController) {
|
||||
this._iosViewController = controller;
|
||||
(<any>this)._nativeView = this.nativeView = controller.view;
|
||||
initNativeView(this);
|
||||
this.setNativeView((<any>this)._nativeView = controller.view);
|
||||
}
|
||||
|
||||
public disposeNativeView() {
|
||||
this._iosViewController = undefined;
|
||||
this.nativeView = undefined;
|
||||
this.setNativeView(undefined);
|
||||
}
|
||||
|
||||
public _update() {
|
||||
|
||||
Reference in New Issue
Block a user