@qooxdoo/framework 8.0.0-beta.2 → 8.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/Manifest.json +1 -1
- package/lib/compiler/compile-info.json +42 -43
- package/lib/compiler/index.js +555 -645
- package/package.json +1 -1
- package/source/class/qx/Class.js +23 -4
- package/source/class/qx/Mixin.js +15 -6
- package/source/class/qx/core/check/AbstractCheck.js +5 -1
- package/source/class/qx/core/check/CheckFactory.js +6 -0
- package/source/class/qx/core/check/DynamicTypeCheck.js +5 -0
- package/source/class/qx/core/property/ExplicitPropertyStorage.js +7 -19
- package/source/class/qx/core/property/IPropertyStorage.js +2 -21
- package/source/class/qx/core/property/Property.js +96 -72
- package/source/class/qx/core/property/SimplePropertyStorage.js +2 -18
- package/source/class/qx/data/MBinding.js +1 -1
- package/source/class/qx/data/SingleValueBinding.js +58 -100
- package/source/class/qx/data/binding/AbstractSegment.js +15 -10
- package/source/class/qx/data/binding/PropNameSegment.js +34 -11
- package/source/class/qx/test/Mixin.js +219 -0
- package/source/class/qx/test/Promise.js +10 -11
- package/source/class/qx/test/core/Property.js +22 -16
- package/source/class/qx/test/data/singlevalue/Async.js +17 -4
- package/source/class/qx/test/performance/Property.js +0 -1
- package/source/class/qx/test/ui/core/SingleSelectionManager.js +150 -0
- package/source/class/qx/tool/compiler/cli/api/CompilerApi.js +1 -2
- package/source/class/qx/ui/core/SingleSelectionManager.js +4 -4
- package/source/class/qx/ui/form/validation/Manager.js +1 -1
- package/source/resource/qx/decoration/Modern/table/boolean-false.png +0 -0
- package/source/resource/qx/decoration/Modern/table/boolean-true.png +0 -0
package/package.json
CHANGED
package/source/class/qx/Class.js
CHANGED
|
@@ -75,7 +75,6 @@ qx.Bootstrap.define("qx.Class", {
|
|
|
75
75
|
event: ["string", "object"], // String or null
|
|
76
76
|
check: null, // Array, String, Function
|
|
77
77
|
transform: null, // String, Function
|
|
78
|
-
async: "boolean", // Boolean
|
|
79
78
|
deferredInit: "boolean", // Boolean
|
|
80
79
|
validate: ["string", "function"], // String, Function
|
|
81
80
|
isEqual: ["string", "function"], // String, Function
|
|
@@ -803,10 +802,18 @@ qx.Bootstrap.define("qx.Class", {
|
|
|
803
802
|
* @param members {Map}
|
|
804
803
|
* The map of members to attach
|
|
805
804
|
*
|
|
805
|
+
* @param events {Map?}
|
|
806
|
+
* The map of events declared by the mixin (used for member validation)
|
|
807
|
+
*
|
|
806
808
|
* @param patch {Boolean ? false}
|
|
807
809
|
* Enable patching
|
|
810
|
+
*
|
|
811
|
+
* @param mixin {qx.Mixin?}
|
|
812
|
+
* The mixin being applied, if any. Used to store per-mixin-per-class base
|
|
813
|
+
* method references so that super calls resolve correctly when the same
|
|
814
|
+
* mixin is included in multiple classes.
|
|
808
815
|
*/
|
|
809
|
-
addMembers(clazz, members, events, patch) {
|
|
816
|
+
addMembers(clazz, members, events, patch, mixin) {
|
|
810
817
|
let proto = clazz.prototype;
|
|
811
818
|
let classOwnMembers = {}; // Track class members to restore after mixin addition
|
|
812
819
|
|
|
@@ -906,6 +913,18 @@ qx.Bootstrap.define("qx.Class", {
|
|
|
906
913
|
member.self = clazz;
|
|
907
914
|
}
|
|
908
915
|
member.base = clazz.prototype[key];
|
|
916
|
+
// Fix: per-mixin-per-class storage to prevent clobbering when the same mixin
|
|
917
|
+
// is included/patched into multiple classes, or when multiple mixins override
|
|
918
|
+
// the same method in the same class.
|
|
919
|
+
if (mixin) {
|
|
920
|
+
if (!clazz.$mixinBases) {
|
|
921
|
+
clazz.$mixinBases = new Map();
|
|
922
|
+
}
|
|
923
|
+
if (!clazz.$mixinBases.has(mixin)) {
|
|
924
|
+
clazz.$mixinBases.set(mixin, {});
|
|
925
|
+
}
|
|
926
|
+
clazz.$mixinBases.get(mixin)[key] = clazz.prototype[key];
|
|
927
|
+
}
|
|
909
928
|
}
|
|
910
929
|
|
|
911
930
|
// Create the storage for this member
|
|
@@ -1127,7 +1146,7 @@ qx.Bootstrap.define("qx.Class", {
|
|
|
1127
1146
|
|
|
1128
1147
|
// Attach members
|
|
1129
1148
|
if (entry.$$members) {
|
|
1130
|
-
qx.Class.addMembers(clazz, entry.$$members, entry.$$events, patch);
|
|
1149
|
+
qx.Class.addMembers(clazz, entry.$$members, entry.$$events, patch, entry);
|
|
1131
1150
|
}
|
|
1132
1151
|
|
|
1133
1152
|
// Attach properties
|
|
@@ -1875,7 +1894,7 @@ qx.Bootstrap.define("qx.Class", {
|
|
|
1875
1894
|
*/
|
|
1876
1895
|
isPropertyInitialized(object, name) {
|
|
1877
1896
|
let property = object.constructor.prototype.$$allProperties[name];
|
|
1878
|
-
return !!(property && property.
|
|
1897
|
+
return !!(property && property.hasLocalValue(object));
|
|
1879
1898
|
},
|
|
1880
1899
|
|
|
1881
1900
|
/**
|
package/source/class/qx/Mixin.js
CHANGED
|
@@ -348,7 +348,7 @@ qx.Bootstrap.define("qx.Mixin", {
|
|
|
348
348
|
// implementation is the first mixin's method
|
|
349
349
|
for (var i = mixedInIndex - 1; i > -1; i--) {
|
|
350
350
|
var peerMixin = mixedInAt.$$flatIncludes[i];
|
|
351
|
-
if (peerMixin.$$members[methodName]) {
|
|
351
|
+
if (peerMixin.$$members && peerMixin.$$members[methodName]) {
|
|
352
352
|
fn = peerMixin.$$members[methodName];
|
|
353
353
|
break;
|
|
354
354
|
}
|
|
@@ -356,12 +356,21 @@ qx.Bootstrap.define("qx.Mixin", {
|
|
|
356
356
|
|
|
357
357
|
// Try looking in the class itself
|
|
358
358
|
if (!fn && mixedInAt.prototype[methodName]) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
359
|
+
// Use per-mixin-per-class storage to avoid clobbering when the same mixin
|
|
360
|
+
// is included in multiple classes or when multiple mixins override the same method
|
|
361
|
+
if (mixedInAt.$mixinBases && mixedInAt.$mixinBases.has(mixin)) {
|
|
362
|
+
fn = mixedInAt.$mixinBases.get(mixin)[methodName] ?? null;
|
|
363
|
+
}
|
|
364
|
+
if (!fn) {
|
|
365
|
+
// Fallback: traverse .base chain (works for patch=true since each patch
|
|
366
|
+
// creates a unique wrapper function with its own .base property)
|
|
367
|
+
fn = mixedInAt.prototype[methodName];
|
|
368
|
+
for (let i = 0; i < mixedInAt.$$flatIncludes.length; i++) {
|
|
369
|
+
if (!mixedInAt.$$flatIncludes[i].$$members || !mixedInAt.$$flatIncludes[i].$$members[methodName]) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
fn = fn.base;
|
|
363
373
|
}
|
|
364
|
-
fn = fn.base;
|
|
365
374
|
}
|
|
366
375
|
}
|
|
367
376
|
|
|
@@ -23,13 +23,17 @@ qx.Bootstrap.define("qx.core.check.AbstractCheck", {
|
|
|
23
23
|
extend: Object,
|
|
24
24
|
implement: qx.core.check.ICheck,
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @param {boolean} nullable
|
|
29
|
+
*/
|
|
26
30
|
construct(nullable) {
|
|
27
31
|
super();
|
|
28
32
|
this.__nullable = !!nullable;
|
|
29
33
|
},
|
|
30
34
|
|
|
31
35
|
members: {
|
|
32
|
-
/** @type{Boolean} whether null is allowed */
|
|
36
|
+
/** @type {Boolean} whether null is allowed */
|
|
33
37
|
__nullable: null,
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -68,6 +68,12 @@ qx.Bootstrap.define("qx.core.check.CheckFactory", {
|
|
|
68
68
|
expr = expr.replace(/<.*>/g, ""); // remove generics
|
|
69
69
|
|
|
70
70
|
let checkname = expr;
|
|
71
|
+
if (checkname.includes("||")) {
|
|
72
|
+
//For now, if the typename contains ||, we just accept it.
|
|
73
|
+
//That's what it was like in version 7
|
|
74
|
+
return new qx.core.check.Any();
|
|
75
|
+
}
|
|
76
|
+
|
|
71
77
|
if (checkname.endsWith("?")) {
|
|
72
78
|
checkname = checkname.substring(0, checkname.length - 1);
|
|
73
79
|
nullable = true;
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
qx.Bootstrap.define("qx.core.check.DynamicTypeCheck", {
|
|
23
23
|
extend: qx.core.check.AbstractCheck,
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @param {string} typename
|
|
28
|
+
* @param {boolean} nullable
|
|
29
|
+
*/
|
|
25
30
|
construct(typename, nullable) {
|
|
26
31
|
super(nullable);
|
|
27
32
|
this.__typename = typename;
|
|
@@ -25,16 +25,10 @@ qx.Bootstrap.define("qx.core.property.ExplicitPropertyStorage", {
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* @param {qx.core.property.Property} property
|
|
28
|
-
* @param {Function} clazz Qooxdoo class which the property relates to
|
|
29
28
|
*/
|
|
30
|
-
construct(property
|
|
29
|
+
construct(property) {
|
|
31
30
|
super();
|
|
32
|
-
let def = property.getDefinition();
|
|
33
31
|
this.__property = property;
|
|
34
|
-
this.__get = def.get;
|
|
35
|
-
this.__getAsync = def.getAsync || def.get;
|
|
36
|
-
this.__set = def.set;
|
|
37
|
-
this.__setAsync = def.setAsync || def.set;
|
|
38
32
|
},
|
|
39
33
|
|
|
40
34
|
members: {
|
|
@@ -42,32 +36,26 @@ qx.Bootstrap.define("qx.core.property.ExplicitPropertyStorage", {
|
|
|
42
36
|
* @type {qx.core.property.Property}
|
|
43
37
|
*/
|
|
44
38
|
__property: null,
|
|
39
|
+
|
|
45
40
|
/**
|
|
46
41
|
* @Override
|
|
47
42
|
*/
|
|
48
43
|
get(thisObj, property) {
|
|
49
|
-
return this.
|
|
44
|
+
return this.__property.getDefinition().get.call(thisObj, property, thisObj);
|
|
50
45
|
},
|
|
51
46
|
|
|
52
47
|
/**
|
|
53
48
|
* @Override
|
|
54
49
|
*/
|
|
55
50
|
getAsync(thisObj, property) {
|
|
56
|
-
return this.
|
|
51
|
+
return this.__property.getDefinition().getAsync.call(thisObj, property, thisObj);
|
|
57
52
|
},
|
|
58
53
|
|
|
59
54
|
/**
|
|
60
55
|
* @Override
|
|
61
56
|
*/
|
|
62
57
|
set(thisObj, property, value) {
|
|
63
|
-
this.
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @Override
|
|
68
|
-
*/
|
|
69
|
-
setAsync(thisObj, property, value) {
|
|
70
|
-
return this.__setAsync.call(thisObj, property, value);
|
|
58
|
+
this.__property.getDefinition().set.call(thisObj, value, property, thisObj);
|
|
71
59
|
},
|
|
72
60
|
|
|
73
61
|
/**
|
|
@@ -80,8 +68,8 @@ qx.Bootstrap.define("qx.core.property.ExplicitPropertyStorage", {
|
|
|
80
68
|
/**
|
|
81
69
|
* @Override
|
|
82
70
|
*/
|
|
83
|
-
|
|
84
|
-
return this.__property.
|
|
71
|
+
supportsGetAsync() {
|
|
72
|
+
return !!this.__property.getDefinition().getAsync;
|
|
85
73
|
}
|
|
86
74
|
}
|
|
87
75
|
});
|
|
@@ -48,25 +48,6 @@ qx.Interface.define("qx.core.property.IPropertyStorage", {
|
|
|
48
48
|
*/
|
|
49
49
|
set(thisObj, property, value) {},
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
* Sets a value asynchronously
|
|
53
|
-
*
|
|
54
|
-
* @param {qx.core.Object} thisObj
|
|
55
|
-
* @param {qx.core.propety.IProperty} property the property to set the value of
|
|
56
|
-
* @param {*} value
|
|
57
|
-
*/
|
|
58
|
-
async setAsync(thisObj, property, value) {},
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Called when the property is reset; for standard properties, this just passes through to
|
|
62
|
-
* `set`, but pseudo properties may need to call the implemented `reset` method
|
|
63
|
-
*
|
|
64
|
-
* @param {qx.core.Object} thisObj
|
|
65
|
-
* @param {qx.core.propety.IProperty} property the property to set the value of
|
|
66
|
-
* @param {*} value
|
|
67
|
-
*/
|
|
68
|
-
reset(thisObj, property, value) {},
|
|
69
|
-
|
|
70
51
|
/**
|
|
71
52
|
* Deletes the value
|
|
72
53
|
*
|
|
@@ -76,8 +57,8 @@ qx.Interface.define("qx.core.property.IPropertyStorage", {
|
|
|
76
57
|
dereference(thisObj, property) {},
|
|
77
58
|
|
|
78
59
|
/**
|
|
79
|
-
* Returns whether the storage supports asynchronous
|
|
60
|
+
* Returns whether the storage supports asynchronous getting.
|
|
80
61
|
*/
|
|
81
|
-
|
|
62
|
+
supportsGetAsync() {}
|
|
82
63
|
}
|
|
83
64
|
});
|
|
@@ -41,19 +41,20 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
41
41
|
* will return null, instead of throwing an error.
|
|
42
42
|
*/
|
|
43
43
|
"qx.core.property.Property.inheritableDefaultIsNull": false,
|
|
44
|
+
|
|
44
45
|
/**
|
|
45
46
|
* If set to true, then properties with init values will have their apply method called during construction.
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
"qx.core.property.Property.applyDuringConstruct":
|
|
47
|
+
* You should only set this to false if you have a lot of legacy code which would take too much time/effort to modify to work with the new behavior.
|
|
48
|
+
*/
|
|
49
|
+
"qx.core.property.Property.applyDuringConstruct": true,
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
* Only relevant when applyDuringConstruct is true.
|
|
52
53
|
* This contains regexes matching classnames which are excluded from the auto apply behaviour.
|
|
53
54
|
* They refer to concrete classes only, not the superclasses.
|
|
54
55
|
*
|
|
55
|
-
* Currently (2025-
|
|
56
|
-
* would create problems which are difficult to fix.
|
|
56
|
+
* Currently (2025-DEC-03), only "qx." classes are excluded because enabling applyDuringConstruct
|
|
57
|
+
* would create problems which are too difficult and time-consuming to fix.
|
|
57
58
|
*
|
|
58
59
|
* @type {Array<RegExp | string>}
|
|
59
60
|
*/
|
|
@@ -189,10 +190,11 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
189
190
|
|
|
190
191
|
// Figure out the storage implementation
|
|
191
192
|
if (def.storage) {
|
|
193
|
+
//if it's an object, use it directly as storage
|
|
192
194
|
if (qx.Class.hasInterface(def.storage.constructor, qx.core.property.IPropertyStorage)) {
|
|
193
195
|
this.__storage = def.storage;
|
|
194
196
|
} else {
|
|
195
|
-
this.__storage =
|
|
197
|
+
this.__storage = qx.core.property.PropertyStorageFactory.getStorage(def.storage);
|
|
196
198
|
}
|
|
197
199
|
} else {
|
|
198
200
|
if (def.immutable == "replace") {
|
|
@@ -201,7 +203,7 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
201
203
|
} else if (def.check == "Object") {
|
|
202
204
|
this.__storage = qx.core.property.PropertyStorageFactory.getStorage(qx.core.property.ImmutableObjectStorage);
|
|
203
205
|
} else if (def.check == "qx.data.Array") {
|
|
204
|
-
this.__storage = qx.core.property.PropertyStorageFactory.getStorage(qx.core.property.ImmutableDataArrayStorage)
|
|
206
|
+
this.__storage = qx.core.property.PropertyStorageFactory.getStorage(qx.core.property.ImmutableDataArrayStorage);//cbh
|
|
205
207
|
} else {
|
|
206
208
|
throw new Error(
|
|
207
209
|
`${this}: ` + "only `check : 'Array'` and `check : 'Object'` " + "properties may have `immutable : 'replace'`."
|
|
@@ -209,7 +211,7 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
209
211
|
}
|
|
210
212
|
} else {
|
|
211
213
|
if (typeof def.get == "function" || typeof def.getAsync == "function") {
|
|
212
|
-
this.__storage = new qx.core.property.ExplicitPropertyStorage(this
|
|
214
|
+
this.__storage = new qx.core.property.ExplicitPropertyStorage(this);
|
|
213
215
|
} else {
|
|
214
216
|
this.__storage = qx.core.property.PropertyStorageFactory.getStorage(qx.core.property.SimplePropertyStorage);
|
|
215
217
|
}
|
|
@@ -537,6 +539,12 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
537
539
|
return self.resetAsync(this);
|
|
538
540
|
});
|
|
539
541
|
}
|
|
542
|
+
|
|
543
|
+
if (this.__pseudoProperty) {
|
|
544
|
+
this.__supportsGetAsync = this.__clazz.prototype["get" + qx.Bootstrap.firstUp(this.__propertyName) + "Async"] !== undefined;
|
|
545
|
+
} else {
|
|
546
|
+
this.__supportsGetAsync = this.__storage.supportsGetAsync();
|
|
547
|
+
}
|
|
540
548
|
},
|
|
541
549
|
|
|
542
550
|
/**
|
|
@@ -841,35 +849,20 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
841
849
|
}
|
|
842
850
|
}
|
|
843
851
|
|
|
852
|
+
//store the old value
|
|
853
|
+
let oldValue = this.__getImpl(thisObj, true);
|
|
844
854
|
let state = this.getPropertyState(thisObj);
|
|
845
855
|
|
|
856
|
+
//validate and transform the value
|
|
846
857
|
if (this.__validate) {
|
|
847
858
|
this.__callFunction(thisObj, this.__validate, value, this);
|
|
848
859
|
}
|
|
849
860
|
|
|
850
|
-
let oldValue = this.__getImpl(thisObj, true);
|
|
851
|
-
|
|
852
861
|
if (this.__transform) {
|
|
853
862
|
value = this.__callFunction(thisObj, this.__transform, value, oldValue, this.__propertyName);
|
|
854
863
|
}
|
|
855
864
|
|
|
856
|
-
|
|
857
|
-
if (scope == "user") {
|
|
858
|
-
this.__storage.reset(thisObj, this, value);
|
|
859
|
-
} else if (scope == "themed") {
|
|
860
|
-
if (value === undefined) {
|
|
861
|
-
delete state.themeValue;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
value = this.__getImpl(thisObj, true);
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
if (this.isInheritable()) {
|
|
868
|
-
if (value === "inherit") {
|
|
869
|
-
value = state.inheritedValue;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
865
|
+
//perform additional checks if promise
|
|
873
866
|
let check = this.getCheck();
|
|
874
867
|
if (qx.core.Environment.get("qx.debug")) {
|
|
875
868
|
if (!check || check instanceof qx.core.check.Any) {
|
|
@@ -877,13 +870,15 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
877
870
|
this.warn(
|
|
878
871
|
"Property " +
|
|
879
872
|
this +
|
|
880
|
-
" is being set to a promise,
|
|
873
|
+
" is being set to a promise, but its check is not a Promise. The property will be set to the promise itself."
|
|
881
874
|
);
|
|
882
875
|
}
|
|
883
876
|
}
|
|
884
877
|
}
|
|
885
878
|
|
|
886
|
-
|
|
879
|
+
//use check for validation and coercion if necessary,
|
|
880
|
+
//but only if not resetting
|
|
881
|
+
if (!(value === "inherit" && this.isInheritable()) && method !== "reset" && check) {
|
|
887
882
|
if (!(value === null && oldValue === null) && !check.matches(value, thisObj)) {
|
|
888
883
|
let coerced = check.coerce(value, thisObj);
|
|
889
884
|
if (qx.lang.Type.isPromise(value) || coerced === null || !check.matches(coerced, thisObj)) {
|
|
@@ -893,6 +888,39 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
893
888
|
}
|
|
894
889
|
}
|
|
895
890
|
|
|
891
|
+
//Modify the value in the underlying storage
|
|
892
|
+
if (method == "reset") {
|
|
893
|
+
if (scope == "user") {
|
|
894
|
+
this.__storage.set(thisObj, this, value);
|
|
895
|
+
} else if (scope == "themed") {
|
|
896
|
+
if (value === undefined) {
|
|
897
|
+
delete state.themeValue;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
value = this.__getImpl(thisObj, true);
|
|
901
|
+
} else {
|
|
902
|
+
if (scope == "user") {
|
|
903
|
+
// Always set the value to the storage if it is a user value; this is because themable properties
|
|
904
|
+
// might be equal now, but if the theme value changes, the user's override needs to remain.
|
|
905
|
+
if (method == "set" || method == "init") {
|
|
906
|
+
this.__storage.set(thisObj, this, value);
|
|
907
|
+
}
|
|
908
|
+
} else if (scope == "themed") {
|
|
909
|
+
if (value !== undefined) {
|
|
910
|
+
state.themeValue = value;
|
|
911
|
+
value = this.__getImpl(thisObj);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// If the value is "inherit" and the property is inheritable, we need to calculate the inherited value
|
|
917
|
+
if (this.isInheritable() && value === "inherit") {
|
|
918
|
+
value = state.inheritedValue;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
//the final value has now been deternined.
|
|
922
|
+
//If it's not equal to the old value or state.applyMethodCalled was not set,
|
|
923
|
+
//we call apply and fire event
|
|
896
924
|
let isEqual = this.isEqual(thisObj, value, oldValue);
|
|
897
925
|
if (!isEqual && this.isMutating(thisObj)) {
|
|
898
926
|
this.warn("Property " + this + " is currently mutating");
|
|
@@ -914,23 +942,6 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
914
942
|
|
|
915
943
|
this.__setMutating(thisObj, true);
|
|
916
944
|
|
|
917
|
-
if (scope == "user") {
|
|
918
|
-
// Always set the value to the storage if it is a user value; this is because themable properties
|
|
919
|
-
// might be equal now, but if the theme value changes, the user's override needs to remain.
|
|
920
|
-
if (method == "set" || method == "init") {
|
|
921
|
-
this.__storage.set(thisObj, this, value);
|
|
922
|
-
}
|
|
923
|
-
} else if (scope == "themed") {
|
|
924
|
-
if (method != "reset") {
|
|
925
|
-
if (value !== undefined) {
|
|
926
|
-
state.themeValue = value;
|
|
927
|
-
value = this.get(thisObj);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
} else if (qx.core.Environment.get("qx.debug")) {
|
|
931
|
-
throw new Error(`Invalid scope=${scope} in ${this.classname}.__setImpl`);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
945
|
if (value === undefined) {
|
|
935
946
|
value = null;
|
|
936
947
|
}
|
|
@@ -976,21 +987,22 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
976
987
|
|
|
977
988
|
let state = this.getPropertyState(thisObj);
|
|
978
989
|
|
|
990
|
+
//store the old value
|
|
991
|
+
//we get it synchronously here because if this property supports async get,
|
|
992
|
+
//we may not always want to fetch the value asynchronously
|
|
993
|
+
//this means the apply method will be called with undefined as oldValue
|
|
994
|
+
let oldValue = this.__getImpl(thisObj, true);
|
|
995
|
+
|
|
996
|
+
//validate and transform the value
|
|
979
997
|
if (this.__validate) {
|
|
980
998
|
this.__callFunction(thisObj, this.__validate, value, this);
|
|
981
999
|
}
|
|
982
1000
|
|
|
983
|
-
let oldValue = await this.__getAsyncImpl(thisObj, true);
|
|
984
1001
|
|
|
985
1002
|
if (this.__transform) {
|
|
986
1003
|
value = this.__callFunction(thisObj, this.__transform, value, oldValue, this.__propertyName);
|
|
987
1004
|
}
|
|
988
1005
|
|
|
989
|
-
if (method == "reset") {
|
|
990
|
-
this.__storage.reset(thisObj, this, value);
|
|
991
|
-
value = await this.__getAsyncImpl(thisObj, true);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
1006
|
let check = this.getCheck();
|
|
995
1007
|
if (qx.core.Environment.get("qx.debug")) {
|
|
996
1008
|
if (!check || check instanceof qx.core.check.Any) {
|
|
@@ -1014,6 +1026,22 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1014
1026
|
}
|
|
1015
1027
|
}
|
|
1016
1028
|
|
|
1029
|
+
//Actually modify the underlying storage
|
|
1030
|
+
if (method == "reset") {
|
|
1031
|
+
this.__storage.set(thisObj, this, value);
|
|
1032
|
+
value = this.__getImpl(thisObj, true);
|
|
1033
|
+
} else {
|
|
1034
|
+
if (scope == "user") {
|
|
1035
|
+
// Always set the value to the storage if it is a user value; this is because themable properties
|
|
1036
|
+
// might be equal now, but if the theme value changes, the user's override needs to remain.
|
|
1037
|
+
if (method == "set" || method == "init") {
|
|
1038
|
+
this.__storage.set(thisObj, this, value);
|
|
1039
|
+
}
|
|
1040
|
+
} else if (qx.core.Environment.get("qx.debug")) {
|
|
1041
|
+
throw new Error(`Invalid scope=${scope} in ${this.classname}.__setAsyncImpl`);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1017
1045
|
let isEqual = this.isEqual(thisObj, value, oldValue);
|
|
1018
1046
|
if (!isEqual && this.isMutating(thisObj)) {
|
|
1019
1047
|
this.warn("Property " + this + " is currently mutating");
|
|
@@ -1037,16 +1065,6 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1037
1065
|
let promise = new Promise(r => (resolve = r));
|
|
1038
1066
|
this.__setMutating(thisObj, promise);
|
|
1039
1067
|
|
|
1040
|
-
if (scope == "user") {
|
|
1041
|
-
// Always set the value to the storage if it is a user value; this is because themable properties
|
|
1042
|
-
// might be equal now, but if the theme value changes, the user's override needs to remain.
|
|
1043
|
-
if (method == "set" || method == "init") {
|
|
1044
|
-
this.__storage.set(thisObj, this, value);
|
|
1045
|
-
}
|
|
1046
|
-
} else if (qx.core.Environment.get("qx.debug")) {
|
|
1047
|
-
throw new Error(`Invalid scope=${scope} in ${this.classname}.__setAsyncImpl`);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
1068
|
if (value === undefined) {
|
|
1051
1069
|
value = null;
|
|
1052
1070
|
}
|
|
@@ -1075,8 +1093,6 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1075
1093
|
|
|
1076
1094
|
if (value !== undefined) {
|
|
1077
1095
|
return value;
|
|
1078
|
-
} else if (this.isAsync()) {
|
|
1079
|
-
return undefined;
|
|
1080
1096
|
}
|
|
1081
1097
|
|
|
1082
1098
|
let state = this.getPropertyState(thisObj);
|
|
@@ -1105,7 +1121,7 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1105
1121
|
|
|
1106
1122
|
if (value !== undefined) {
|
|
1107
1123
|
return value;
|
|
1108
|
-
} else if (this.
|
|
1124
|
+
} else if (this.__supportsGetAsync) {
|
|
1109
1125
|
if (!safe) {
|
|
1110
1126
|
throw new Error("Property " + this + " has not been initialized - try using getAsync() instead");
|
|
1111
1127
|
} else {
|
|
@@ -1133,6 +1149,11 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1133
1149
|
* @returns {Promise<*>}
|
|
1134
1150
|
*/
|
|
1135
1151
|
async __getAsyncImpl(thisObj, safe) {
|
|
1152
|
+
if (qx.core.Environment.get("qx.debug")) {
|
|
1153
|
+
if (!this.__supportsGetAsync) {
|
|
1154
|
+
this.warn(`Called getAsync/resetAsync in property ${this} which does not support async getter.`);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1136
1157
|
const getRaw = async () => {
|
|
1137
1158
|
let value = this.__storage.get(thisObj, this);
|
|
1138
1159
|
|
|
@@ -1140,12 +1161,14 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1140
1161
|
return value;
|
|
1141
1162
|
}
|
|
1142
1163
|
|
|
1143
|
-
value =
|
|
1164
|
+
value = this.getInitValue(thisObj);
|
|
1144
1165
|
if (value !== undefined) {
|
|
1145
1166
|
return value;
|
|
1146
1167
|
}
|
|
1147
1168
|
|
|
1148
|
-
|
|
1169
|
+
if (this.__supportsGetAsync) {
|
|
1170
|
+
return await this.__storage.getAsync(thisObj, this);
|
|
1171
|
+
}
|
|
1149
1172
|
};
|
|
1150
1173
|
|
|
1151
1174
|
let value = await getRaw();
|
|
@@ -1383,23 +1406,24 @@ qx.Bootstrap.define("qx.core.property.Property", {
|
|
|
1383
1406
|
promiseReady(thisObj) {},
|
|
1384
1407
|
|
|
1385
1408
|
/**
|
|
1386
|
-
* Whether the property
|
|
1409
|
+
* Whether the property has a defined value stored locally,
|
|
1410
|
+
* meaning we can safely get it synchronously
|
|
1387
1411
|
*
|
|
1388
1412
|
* @param {qx.core.Object} thisObj the object on which the property is defined
|
|
1389
1413
|
* @return {Boolean}
|
|
1390
1414
|
*/
|
|
1391
|
-
|
|
1415
|
+
hasLocalValue(thisObj) {
|
|
1392
1416
|
let value = this.__getImpl(thisObj, true);
|
|
1393
1417
|
return value !== undefined;
|
|
1394
1418
|
},
|
|
1395
1419
|
|
|
1396
1420
|
/**
|
|
1397
|
-
* Whether the property supports
|
|
1421
|
+
* Whether the property supports getting asynchronously from the underlying storage
|
|
1398
1422
|
*
|
|
1399
1423
|
* @return {Boolean}
|
|
1400
1424
|
*/
|
|
1401
|
-
|
|
1402
|
-
return
|
|
1425
|
+
supportsGetAsync() {
|
|
1426
|
+
return this.__supportsGetAsync;
|
|
1403
1427
|
},
|
|
1404
1428
|
|
|
1405
1429
|
/**
|
|
@@ -37,7 +37,7 @@ qx.Bootstrap.define("qx.core.property.SimplePropertyStorage", {
|
|
|
37
37
|
* @Override
|
|
38
38
|
*/
|
|
39
39
|
async getAsync(thisObj, property) {
|
|
40
|
-
|
|
40
|
+
// nothing
|
|
41
41
|
},
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -52,22 +52,6 @@ qx.Bootstrap.define("qx.core.property.SimplePropertyStorage", {
|
|
|
52
52
|
data.value = value;
|
|
53
53
|
},
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
* @Override
|
|
57
|
-
*/
|
|
58
|
-
async setAsync(thisObj, property, value) {
|
|
59
|
-
return qx.Promise.resolve(value).then(value => {
|
|
60
|
-
this.set(thisObj, property, value);
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @Override
|
|
66
|
-
*/
|
|
67
|
-
reset(thisObj, property, value) {
|
|
68
|
-
this.set(thisObj, property, value);
|
|
69
|
-
},
|
|
70
|
-
|
|
71
55
|
/**
|
|
72
56
|
* @Override
|
|
73
57
|
*/
|
|
@@ -78,7 +62,7 @@ qx.Bootstrap.define("qx.core.property.SimplePropertyStorage", {
|
|
|
78
62
|
/**
|
|
79
63
|
* @Override
|
|
80
64
|
*/
|
|
81
|
-
|
|
65
|
+
supportsGetAsync() {
|
|
82
66
|
return false;
|
|
83
67
|
},
|
|
84
68
|
|
|
@@ -77,7 +77,7 @@ qx.Mixin.define("qx.data.MBinding", {
|
|
|
77
77
|
*/
|
|
78
78
|
bindAsync: qx.core.Environment.select("qx.promise", {
|
|
79
79
|
true(sourcePropertyChain, targetObject, targetProperty, options) {
|
|
80
|
-
var binding = qx.data.SingleValueBinding.bind(this, sourcePropertyChain, targetObject, targetProperty || "value", options);
|
|
80
|
+
var binding = qx.data.SingleValueBinding.bind(this, sourcePropertyChain, targetObject, targetProperty || "value", {...options, async: true});
|
|
81
81
|
return binding.getInitPromise().then(() => binding);
|
|
82
82
|
},
|
|
83
83
|
false(sourcePropertyChain, targetObject, targetProperty, options) {
|