8000 CssState will aggregate changes and batch at once, properties will no… · jensWorkGit/NativeScript@0b0598b · GitHub
[go: up one dir, main page]

Skip to content

Commit 0b0598b

Browse files
committed
CssState will aggregate changes and batch at once, properties will not fire changes multiple times, animations should not reset-and-apply.
1 parent 15fa8fa commit 0b0598b

File tree

12 files changed

+250
-258
lines changed

12 files changed

+250
-258
lines changed

apps/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"nativescript": {
77
"id": "org.nativescript.apps",
88
"tns-ios": {
9-
"version": "2.4.0"
9+
"version": "2.5.0"
1010
},
1111
"tns-android": {
1212
"version": "2.4.1"
@@ -24,4 +24,4 @@
2424
"nativescript-dev-typescript": "^0.3.0",
2525
"typescript": "~2.0.10"
2626
}
27-
}
27+
}

tests/app/ui/styling/style-tests.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import * as helper from "../../ui/helper";
1010
import * as types from "utils/types";
1111
import * as viewModule from "ui/core/view";
1212
import { resolveFileNameFromUrl } from "ui/styling/style-scope";
13-
import { unsetValue } from "ui/core/view";
13+
import { unsetValue, backgroundColorProperty } from "ui/core/view";
14+
import { Button } from "ui/button";
1415

1516
export function test_css_dataURI_is_applied_to_backgroundImageSource() {
1617
const stack = new stackModule.StackLayout();
@@ -1457,3 +1458,23 @@ export function test_resolveFileNameFromUrl_unexisting_file() {
14571458
// <snippet module="ui/styling" title="styling">
14581459
// For information and example how to use style properties please refer to special [**Styling**](../../../styling.md) topic.
14591460
// </snippet>
1461+
1462+
export function test_css_doesnt_set_multiple_times() {
1463+
const button = new Button();
1464+
button.id = "login";
1465+
button.className = "big";
1466+
let setterCount = 0;
1467+
Object.defineProperty(button, backgroundColorProperty.native, {
1468+
enumerable: true,
1469+
configurable: true,
1470+
get: () => null,
1471+
set: value => setterCount++
1472+
})
1473+
1474+
helper.buildUIAndRunTest(button, function (views: Array<viewModule.View>) {
1475+
const page = <pageModule.Page>views[1];
1476+
const expected = "horizontal";
1477+
page.css = `Button { background-color: red; } .big { background-color: green; } #login { background-color: blue; }`;
1478+
TKUnit.assertEqual(setterCount, 1, "Native setter expected to be called once.");
1479+
});
1480+
}

tns-core-modules/tns-core-modules.base.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
/// <reference path="text/text.d.ts" />
3131
/// <reference path="timer/timer.d.ts" />
3232
/// <reference path="trace/trace.d.ts" />
33-
/// <reference path="ui/definitions.d.ts" />
33+
/// <reference path="ui/core/properties.d.ts" />
34+
/// <reference path="ui/core/view-base.d.ts" />
3435
/// <reference path="ui/action-bar/action-bar.d.ts" />
3536
/// <reference path="ui/activity-indicator/activity-indicator.d.ts" />
3637
/// <reference path="ui/animation/animation.d.ts" />

tns-core-modules/ui/animation/keyframe-animation.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
declare module "ui/animation/keyframe-animation" {
2-
import { View } from "ui/core/view";
2+
import { ViewBase } from "ui/core/view";
33

44
export interface KeyframeDeclaration {
55
property: string;
@@ -83,7 +83,7 @@ declare module "ui/animation/keyframe-animation" {
8383
/**
8484
* Plays the animation.
8585
*/
86-
public play: (view: View) => Promise<void>;
86+
public play: (view: ViewBase) => Promise<void>;
8787

8888
/**
8989
* Cancels a playing animation.
@@ -93,6 +93,6 @@ declare module "ui/animation/keyframe-animation" {
9393
/**
9494
* Creates a keyframe animation from animation definition.
9595
*/
96-
public static keyframeAnimationFromInfo(info: KeyframeAnimationInfo);
96+
public static keyframeAnimationFromInfo(info: KeyframeAnimationInfo): KeyframeAnimation;
9797
}
9898
}

tns-core-modules/ui/animation/keyframe-animation.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "ui/animation/keyframe-animation";
77

88
import {
9-
View,
9+
ViewBase,
1010
backgroundColorProperty,
1111
scaleXProperty,
1212
scaleYProperty,
@@ -53,17 +53,23 @@ interface Keyframe {
5353
forceLayer?: boolean;
5454
}
5555

56+
declare module "ui/animation/keyframe-animation" {
57+
export interface KeyframeAnimation {
58+
animations: Keyframe[];
59+
}
60+
}
61+
5662
export class KeyframeAnimation implements KeyframeAnimationDefinition {
57-
public animations: Array<Keyframe>;
63+
public animations: Keyframe[];
5864
public delay: number = 0;
5965
public iterations: number = 1;
6066

6167
private _resolve;
6268
private _reject;
6369
private _isPlaying: boolean;
6470
private _isForwards: boolean;
65-
private _nativeAnimations: Array<Animation>;
66-
private _target: View;
71+
private _nativeAnimations: Animation[];
72+
private _target: ViewBase;
6773

6874
public static keyframeAnimationFromInfo(info: KeyframeAnimationInfo) {
6975
let animations = new Array<Keyframe>();
@@ -145,7 +151,7 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
145151
}
146152
}
147153

148-
public play(view: View): Promise<void> {
154+
public play(view: ViewBase): Promise<void> {
149155
if (this._isPlaying) {
150156
throw new Error("Animation is already playing.");
151157
}
@@ -169,7 +175,7 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
169175
return animationFinishedPromise;
170176
}
171177

172-
private animate(view: View, index: number, iterations: number) {
178+
private animate(view: ViewBase, index: number, iterations: number) {
173179
if (!this._isPlaying) {
174180
return;
175181
}
@@ -234,7 +240,7 @@ export class KeyframeAnimation implements KeyframeAnimationDefinition {
234240
this._reject(new Error("Animation cancelled."));
235241
}
236242

237-
private _resetAnimationValues(view: View, animation: Object) {
243+
private _resetAnimationValues(view: ViewBase, animation: Object) {
238244
if ("backgroundColor" in animation) {
239245
view.style[backgroundColorProperty.keyframe] = unsetValue;
240246
}
Lines changed: 111 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,111 @@
1-
// declare module "ui/core/properties" {
2-
// import {ViewBase} from "ui/coew/viewbase";
3-
// import {Style} from "ui/styling/style";
4-
5-
// interface PropertyOptions<T, U> {
6-
// name: string,
7-
// defaultValue?: U,
8-
// affectsLayout?: boolean,
9-
// equalityComparer?: (x: U, y: U) => boolean,
10-
// valueChanged?: (target: T, oldValue: U, newValue: U) => void,
11-
// valueConverter?: (value: any) => U
12-
// }
13-
14-
// interface CssPropertyOptions<T extends Style, U> extends PropertyOptions<T, U> {
15-
// cssName: string;
16-
// }
17-
18-
// class Property<T extends ViewBase, U> implements PropertyDescriptor {
19-
// constructor(options: PropertyOptions<T, U>);
20-
21-
// public native: symbol;
22-
// public register(cls: { prototype: T }): void;
23-
// }
24-
25-
// class InheritedProperty<T extends ViewBase, U> extends Property<T, U> {
26-
// constructor(options: PropertyOptions<T, U>);
27-
// }
28-
29-
// class CssProperty<T extends Style, U> {
30-
// constructor(options: CssPropertyOptions<T, U>);
31-
32-
// public native: symbol;
33-
// public cssName: string;
34-
// public register(cls: { prototype: T }): void;
35-
// }
36-
37-
// class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U> {
38-
// constructor(options: CssPropertyOptions<T, U>);
39-
// }
40-
// }
1+
declare module "ui/core/properties" {
2+
import { ViewBase } from "ui/core/view-base";
3+
import { Style } from "ui/styling/style";
4+
5+
//@private
6+
export function _isSet(cssProperty: CssProperty<any, any>, instance: Style): boolean;
7+
//@endprivate
8+
9+
/**
10+
* Value specifing that Property should be set to its initial value.
11+
*/
12+
export const unsetValue: any;
13+
14+
export interface PropertyOptions<T, U> {
15+
readonly name: string;
16+
readonly defaultValue?: U;
17+
readonly affectsLayout?: boolean;
18+
readonly equalityComparer?: (x: U, y: U) => boolean;
19+
readonly valueChanged?: (target: T, oldValue: U, newValue: U) => void;
20+
readonly valueConverter?: (value: string) => U;
21+
}
22+
23+
export interface CoerciblePropertyOptions<T, U> extends PropertyOptions<T, U> {
24+
readonly coerceValue: (t: T, u: U) => U;
25+
}
26+
27+
export interface CssPropertyOptions<T extends Style, U> extends PropertyOptions<T, U> {
28+
readonly cssName: string;
29+
}
30+
31+
export interface CssAnimationPropertyOptions<T, U> {
32+
readonly name: string;
33+
readonly cssName?: string;
34+
readonly defaultValue?: U;
35+
readonly equalityComparer?: (x: U, y: U) => boolean;
36+
readonly valueChanged?: (target: T, oldValue: U, newValue: U) => void;
37+
readonly valueConverter?: (value: string) => U;
38+
}
39+
40+
export class CssAnimationProperty<T extends Style, U> {
41+
constructor(options: CssAnimationPropertyOptions<T, U>);
42+
43+
public readonly name: string;
44+
public readonly cssName: string;
45+
public readonly native: symbol;
46+
47+
readonly keyframe: string;
48+
49+
public register(cls: { prototype: T }): void;
50+
51+
public _valueConverter?: (value: string) => any;
52+
public static _getByCssName(name: string): CssAnimationProperty<any, any>;
53+
}
54+
55+
export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<U> {
56+
constructor(options: PropertyOptions<T, U>);
57+
58+
public readonly native: symbol;
59+
public readonly defaultValue: U;
60+
public register(cls: { prototype: T }): void;
61+
public nativeValueChange(T, U): void;
62+
}
63+
64+
export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> implements TypedPropertyDescriptor<U> {
65+
constructor(options: CoerciblePropertyOptions<T, U>);
66+
67+
public readonly coerce: (target: T) => void;
68+
}
69+
70+
export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> {
71+
constructor(options: PropertyOptions<T, U>);
72+
}
73+
74+
export class CssProperty<T extends Style, U> {
75+
constructor(options: CssPropertyOptions<T, U>);
76+
77+
public readonly native: symbol;
78+
public readonly name: string;
79+
public readonly cssName: string;
80+
public readonly cssLocalName: string;
81+
public readonly defaultValue: U;
82+
public register(cls: { prototype: T }): void;
83+
}
84+
85+
export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U> {
86+
constructor(options: CssPropertyOptions<T, U>);
87+
}
88+
89+
export interface ShorthandPropertyOptions<P> {
90+
readonly name: string,
91+
readonly cssName: string;
92+
readonly converter: (value: string | P) => [CssProperty<any, any>, any][];
93+
readonly getter: (this: Style) => string | P;
94+
}
95+
96+
export class ShorthandProperty<T extends Style, P> {
97+
constructor(options: ShorthandPropertyOptions<P>);
98+
99+
public readonly native: symbol;
100+
public readonly name: string;
101+
public readonly cssName: string;
102+
public register(cls: { prototype: T }): void;
103+
public static _split(kvp: { property: string, value: string }): [CssProperty<any, any>, any][];
104+
}
105+
106+
export function initNativeView(view: ViewBase): void;
107+
export function resetNativeView(view: ViewBase): void;
108+
109+
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
110+
export function makeParser<T>(isValid: (value: any) => boolean): (value: any) => T;
111+
}

tns-core-modules/ui/core/properties.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
546546
export class CssAnimationProperty<T extends Style, U> {
547547
public readonly name: string;
548548
public readonly cssName: string;
549+
public readonly cssLocalName: string;
549550

550551
public readonly native: symbol;
551552
public readonly register: (cls: { prototype }) => void;
@@ -565,7 +566,10 @@ export class CssAnimationProperty<T extends Style, U> {
565566
CssAnimationProperty.properties[options.cssName || propertyName] = this;
566567
this._valueConverter = options.valueConverter;
567568

568-
const cssName = "css:" + (options.cssName || propertyName);
569+
const cssLocalName = (options.cssName || propertyName);
570+
this.cssLocalName = cssLocalName;
571+
572+
const cssName = "css:" + cssLocalName;
569573
this.cssName = cssName;
570574

571575
const keyframeName = "keyframe:" + propertyName;
@@ -801,6 +805,9 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
801805
public readonly native: symbol;
802806
public readonly sourceKey: symbol;
803807

808+
private readonly _converter: (value: string | P) => [definitions.CssProperty<any, any>, any][];
809+
private static shorthandProperties: { [cssName: string]: ShorthandProperty<any, any> } = {};
810+
804811
constructor(options: definitions.ShorthandPropertyOptions<P>) {
805812
this.name = options.name;
806813

@@ -810,7 +817,10 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
810817
this.cssName = `css:${options.cssName}`;
811818
this.cssLocalName = `${options.cssName}`;
812819

820+
ShorthandProperty.shorthandProperties[this.cssLocalName] = this;
821+
813822
const converter = options.converter;
823+
this._converter = converter;
814824

815825
function setLocalValue(this: T, value: string | P): void {
816826
if (this[key] !== value) {
@@ -858,6 +868,18 @@ export class ShorthandProperty<T extends Style, P> implements definitions.Shorth
858868
Object.defineProperty(cls.prototype, this.cssLocalName, this.localValueDescriptor);
859869
}
860870
}
871+
872+
public static _getByCssName(cssName: string): ShorthandProperty<any, any> {
873+
return ShorthandProperty.shorthandProperties[cssName];
874+
}
875+
876+
public static _split(kvp: { property: string, value: string }): [definitions.CssProperty<any, any>, any][] {
877+
let property = ShorthandProperty.shorthandProperties[kvp.property];
878+
if (property) {
879+
return property._converter(kvp.value);
880+
}
881+
return null;
882+
}
861883
}
862884

863885
function inheritablePropertyValuesOn(view: ViewBase): Array<{ property: InheritedProperty<any, any>, value: any }> {
@@ -986,19 +1008,6 @@ export function clearInheritedProperties(view: ViewBase): void {
9861008
}
9871009
}
9881010

989-
export function resetCSSProperties(style: Style): void {
990-
let symbols = (<any>Object).getOwnPropertySymbols(style);
991-
for (let symbol of symbols) {
992-
let cssProperty;
993-
if (cssProperty = cssSymbolPropertyMap[symbol]) {
994-
style[cssProperty.cssName] = unsetValue;
995-
if (cssProperty instanceof CssAnimationProperty) {
996-
style[cssProperty.keyframe] = unsetValue;
997-
}
998-
}
999-
}
1000-
}
1001-
10021011
export function propagateInheritedProperties(view: ViewBase): void {
10031012
const inheritablePropertyValues = inheritablePropertyValuesOn(view);
10041013
const inheritableCssPropertyValues = inheritableCssPropertyValuesOn(view.style);

0 commit comments

Comments
 (0)
0