mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-03 23:55:04 +08:00
New LinearColorEntity, string serialization fixed
This commit is contained in:
454
dist/ueblueprint.js
vendored
454
dist/ueblueprint.js
vendored
@@ -214,6 +214,33 @@ class IInput {
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
* @typedef {(new (object?: Object) => IEntity) | StringConstructor | NumberConstructor | BooleanConstructor} Constructor
|
||||
* @typedef {Constructor|Constructor[]} AcceptedType
|
||||
*/
|
||||
|
||||
class SerializedType {
|
||||
|
||||
/** @type {(Constructor|Array<Constructor>)[]} */
|
||||
#types
|
||||
get types() {
|
||||
return this.#types
|
||||
}
|
||||
set types(v) {
|
||||
this.#types = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {...AcceptedType} acceptedTypes
|
||||
*/
|
||||
constructor(...acceptedTypes) {
|
||||
this.#types = acceptedTypes;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
@@ -249,21 +276,21 @@ class TypeInitialization {
|
||||
if (targetType === undefined) {
|
||||
targetType = value?.constructor;
|
||||
}
|
||||
let wrongType = false;
|
||||
if (targetType && value?.constructor !== targetType && !(value instanceof targetType)) {
|
||||
wrongType = true;
|
||||
if (
|
||||
targetType
|
||||
&& targetType !== SerializedType
|
||||
&& !(value?.constructor === targetType || value instanceof targetType)
|
||||
) {
|
||||
value = new targetType(value);
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
|
||||
value = value.valueOf(); // Get the relative primitive value
|
||||
}
|
||||
if (wrongType) {
|
||||
return new targetType(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {(new () => T) | StringConstructor | NumberConstructor | BooleanConstructor} Constructor
|
||||
* @typedef {(new () => T) | SerializedType | StringConstructor | NumberConstructor | BooleanConstructor} Constructor
|
||||
* @param {Constructor|Array<Constructor>} type
|
||||
* @param {Boolean} showDefault
|
||||
* @param {any} value
|
||||
@@ -272,6 +299,8 @@ class TypeInitialization {
|
||||
if (value === undefined) {
|
||||
if (type instanceof Array) {
|
||||
value = [];
|
||||
} else if (type instanceof SerializedType) {
|
||||
value = "";
|
||||
} else {
|
||||
value = TypeInitialization.sanitize(new type());
|
||||
}
|
||||
@@ -487,13 +516,30 @@ class Utility {
|
||||
}
|
||||
}
|
||||
|
||||
class ISerializable {
|
||||
|
||||
#showAsString = false
|
||||
|
||||
isShownAsString() {
|
||||
return this.#showAsString
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Boolean} v
|
||||
*/
|
||||
setShowAsString(v) {
|
||||
this.#showAsString = v;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
class IEntity {
|
||||
class IEntity extends ISerializable {
|
||||
|
||||
static attributes = {}
|
||||
|
||||
constructor(values) {
|
||||
super();
|
||||
/**
|
||||
* @param {Object} target
|
||||
* @param {Object} properties
|
||||
@@ -532,6 +578,7 @@ class IEntity {
|
||||
const value = Utility.objectGet(values, [property]);
|
||||
if (value !== undefined) {
|
||||
target[property] = TypeInitialization.sanitize(value, defaultType);
|
||||
// We have a value, need nothing more
|
||||
continue
|
||||
}
|
||||
if (defaultValue instanceof TypeInitialization) {
|
||||
@@ -722,6 +769,26 @@ class KeyBindingEntity extends IEntity {
|
||||
|
||||
// @ts-check
|
||||
|
||||
class LinearColorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
R: Number,
|
||||
G: Number,
|
||||
B: Number,
|
||||
A: Number,
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
/** @type {Number} */ this.R;
|
||||
/** @type {Number} */ this.G;
|
||||
/** @type {Number} */ this.B;
|
||||
/** @type {Number} */ this.A;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
class LocalizedTextEntity extends IEntity {
|
||||
|
||||
static lookbehind = "NSLOCTEXT"
|
||||
@@ -802,7 +869,7 @@ class PinEntity extends IEntity {
|
||||
bSerializeAsSinglePrecisionFloat: false,
|
||||
},
|
||||
LinkedTo: new TypeInitialization([PinReferenceEntity], false),
|
||||
DefaultValue: new TypeInitialization(String, false),
|
||||
DefaultValue: new TypeInitialization(new SerializedType(LinearColorEntity, String), false),
|
||||
AutogeneratedDefaultValue: new TypeInitialization(String, false),
|
||||
DefaultObject: new TypeInitialization(ObjectReferenceEntity, false, null),
|
||||
PersistentGuid: GuidEntity,
|
||||
@@ -912,6 +979,14 @@ class PinEntity extends IEntity {
|
||||
getType() {
|
||||
return this.PinType.PinCategory
|
||||
}
|
||||
|
||||
getSubCategory() {
|
||||
return this.PinType.PinSubCategoryObject.path
|
||||
}
|
||||
|
||||
getColorValue() {
|
||||
if (this.PinType.PinSubCategoryObject.path == "/Script/CoreUObject.LinearColor") ;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
@@ -1023,6 +1098,10 @@ var Parsimmon = /*@__PURE__*/getDefaultExportFromCjs(parsimmon_umd_min.exports);
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
let P = Parsimmon;
|
||||
|
||||
class Grammar {
|
||||
@@ -1032,6 +1111,27 @@ class Grammar {
|
||||
static getGrammarForType(r, attributeType, defaultGrammar) {
|
||||
if (attributeType instanceof TypeInitialization) {
|
||||
attributeType = attributeType.type;
|
||||
return Grammar.getGrammarForType(r, attributeType, defaultGrammar)
|
||||
}
|
||||
if (attributeType instanceof SerializedType) {
|
||||
const noStringTypes = attributeType.types.filter(t => t !== String);
|
||||
let result = P.alt(
|
||||
...noStringTypes.map(t =>
|
||||
Grammar.getGrammarForType(r, t).wrap(P.string('"'), P.string('"')).map(
|
||||
/**
|
||||
* @param {IEntity} entity
|
||||
*/
|
||||
entity => {
|
||||
entity.setShowAsString(true); // Showing as string because it is inside a SerializedType
|
||||
return entity
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
if (noStringTypes.length < attributeType.types.length) {
|
||||
result = result.or(r.String); // Separated because it cannot be wrapped into " and "
|
||||
}
|
||||
return result
|
||||
}
|
||||
switch (Utility.getType(attributeType)) {
|
||||
case Boolean:
|
||||
@@ -1054,6 +1154,8 @@ class Grammar {
|
||||
return r.InvariantText
|
||||
case PinReferenceEntity:
|
||||
return r.PinReference
|
||||
case LinearColorEntity:
|
||||
return r.LinearColor
|
||||
case FunctionReferenceEntity:
|
||||
return r.FunctionReference
|
||||
case PinEntity:
|
||||
@@ -1170,7 +1272,7 @@ class Grammar {
|
||||
r.None,
|
||||
...[r.ReferencePath.map(path => new ObjectReferenceEntity({ type: "", path: path }))]
|
||||
.flatMap(referencePath => [
|
||||
referencePath, // version having just path
|
||||
referencePath, // Version having just path
|
||||
referencePath.trim(P.string('"')) // Version having path surround with double quotes
|
||||
]),
|
||||
P.seqMap(
|
||||
@@ -1229,6 +1331,8 @@ class Grammar {
|
||||
})
|
||||
)
|
||||
|
||||
LinearColor = r => Grammar.createMultiAttributeGrammar(r, LinearColorEntity)
|
||||
|
||||
FunctionReference = r => Grammar.createMultiAttributeGrammar(r, FunctionReferenceEntity)
|
||||
|
||||
KeyBinding = r => P.alt(
|
||||
@@ -1289,6 +1393,9 @@ class SerializerFactory {
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
class ISerializer {
|
||||
|
||||
static grammar = Parsimmon.createLanguage(new Grammar())
|
||||
@@ -1302,21 +1409,66 @@ class ISerializer {
|
||||
this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join("."));
|
||||
}
|
||||
|
||||
writeValue(value, fullKey = undefined) {
|
||||
/**
|
||||
* @param {String} value
|
||||
* @returns {T}
|
||||
*/
|
||||
deserialize(value) {
|
||||
return this.read(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
serialize(object, insideString) {
|
||||
insideString ||= object.isShownAsString();
|
||||
let result = this.write(object, insideString);
|
||||
if (object.isShownAsString()) {
|
||||
result = `"${result}"`;
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} value
|
||||
* @returns {T}
|
||||
*/
|
||||
read(value) {
|
||||
throw new Error("Not implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
write(object, insideString) {
|
||||
throw new Error("Not implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String[]} fullKey
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
writeValue(value, fullKey, insideString) {
|
||||
if (value === null) {
|
||||
return "()"
|
||||
}
|
||||
const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v);
|
||||
const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).serialize(v);
|
||||
// This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically)
|
||||
switch (value?.constructor) {
|
||||
case Function:
|
||||
return this.writeValue(value(), fullKey)
|
||||
return this.writeValue(value(), fullKey, insideString)
|
||||
case Boolean:
|
||||
return Utility.FirstCapital(value.toString())
|
||||
case Number:
|
||||
return value.toString()
|
||||
case String:
|
||||
return `"${Utility.encodeString(value)}"`
|
||||
return insideString
|
||||
? `\\"${Utility.encodeString(value)}\\"`
|
||||
: `"${Utility.encodeString(value)}"`
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
return `(${value.map(v => serialize(v) + ",").join("")})`
|
||||
@@ -1329,25 +1481,26 @@ class ISerializer {
|
||||
/**
|
||||
* @param {String[]} key
|
||||
* @param {Object} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
subWrite(key, object) {
|
||||
subWrite(key, object, insideString) {
|
||||
let result = "";
|
||||
let fullKey = key.concat("");
|
||||
const last = fullKey.length - 1;
|
||||
for (const property of Object.getOwnPropertyNames(object)) {
|
||||
fullKey[last] = property;
|
||||
const value = object[property];
|
||||
if (object[property]?.constructor === Object) {
|
||||
if (value?.constructor === Object) {
|
||||
// Recursive call when finding an object
|
||||
result += (result.length ? this.separator : "")
|
||||
+ this.subWrite(fullKey, value);
|
||||
+ this.subWrite(fullKey, value, insideString);
|
||||
} else if (value !== undefined && this.showProperty(object, fullKey, value)) {
|
||||
result += (result.length ? this.separator : "")
|
||||
+ this.prefix
|
||||
+ this.attributeKeyPrinter(fullKey)
|
||||
+ this.attributeValueConjunctionSign
|
||||
+ this.writeValue(value, fullKey);
|
||||
+ this.writeValue(value, fullKey, insideString);
|
||||
}
|
||||
}
|
||||
if (this.trailingSeparator && result.length && fullKey.length === 1) {
|
||||
@@ -1386,6 +1539,9 @@ class ObjectSerializer extends ISerializer {
|
||||
return super.showProperty(object, attributeKey, attributeValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} value
|
||||
*/
|
||||
read(value) {
|
||||
const parseResult = ISerializer.grammar.Object.parse(value);
|
||||
if (!parseResult.status) {
|
||||
@@ -1396,7 +1552,6 @@ class ObjectSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {String} value
|
||||
* @returns {ObjectEntity[]}
|
||||
*/
|
||||
readMultiple(value) {
|
||||
const parseResult = ISerializer.grammar.MultipleObject.parse(value);
|
||||
@@ -1408,16 +1563,17 @@ class ObjectSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {ObjectEntity} object
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
write(object) {
|
||||
let result = `Begin Object Class=${object.Class.path} Name=${this.writeValue(object.Name, "Name")}
|
||||
${this.subWrite([], object)
|
||||
write(object, insideString) {
|
||||
let result = `Begin Object Class=${object.Class.path} Name=${this.writeValue(object.Name, ["Name"], insideString)}
|
||||
${this.subWrite([], object, insideString)
|
||||
+ object
|
||||
.CustomProperties.map(pin =>
|
||||
this.separator
|
||||
+ this.prefix
|
||||
+ "CustomProperties "
|
||||
+ SerializerFactory.getSerializer(PinEntity).write(pin)
|
||||
+ SerializerFactory.getSerializer(PinEntity).serialize(pin)
|
||||
)
|
||||
.join("")}
|
||||
End Object\n`;
|
||||
@@ -1450,7 +1606,7 @@ class Copy extends IInput {
|
||||
}
|
||||
|
||||
copied() {
|
||||
const value = this.blueprint.getNodes(true).map(node => this.serializer.write(node.entity)).join("\n");
|
||||
const value = this.blueprint.getNodes(true).map(node => this.serializer.serialize(node.entity)).join("\n");
|
||||
navigator.clipboard.writeText(value);
|
||||
}
|
||||
}
|
||||
@@ -3101,6 +3257,65 @@ class BoolPinTemplate extends IInputPinTemplate {
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
|
||||
class ColorPinTemplate extends IInputPinTemplate {
|
||||
|
||||
/** @type {HTMLInputElement} */
|
||||
#input
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
setup(pin) {
|
||||
super.setup(pin);
|
||||
this.#input = pin.querySelector(".ueb-pin-input");
|
||||
let self = this;
|
||||
this.onChangeHandler = _ => pin.entity.DefaultValue = self.#input.checked ? "true" : "false";
|
||||
this.#input.addEventListener("change", this.onChangeHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
cleanup(pin) {
|
||||
super.cleanup(pin);
|
||||
this.#input.removeEventListener("change", this.onChangeHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
getInputs(pin) {
|
||||
return [this.#input.checked ? "true" : "false"]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
* @param {String[]?} value
|
||||
*/
|
||||
setInputs(pin, value = []) {
|
||||
pin.entity.DefaultValue = value.length ? value[0] : this.getInput(pin);
|
||||
this.#input.checked = pin.entity.DefaultValue == "true";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
renderInput(pin) {
|
||||
if (pin.isInput()) {
|
||||
return html`
|
||||
<span class="ueb-pin-input" ${pin.entity.DefaultValue == "true" ? "checked" : ""}></span>
|
||||
`
|
||||
}
|
||||
return super.renderInput(pin)
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
@@ -3133,51 +3348,6 @@ class ExecPinTemplate extends PinTemplate {
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
|
||||
class RealPinTemplate extends IInputPinTemplate {
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
* @param {String[]?} values
|
||||
*/
|
||||
setInputs(pin, values = []) {
|
||||
let num = parseFloat(values.length ? values[0] : this.getInput(pin));
|
||||
let updateDefaultValue = true;
|
||||
if (isNaN(num)) {
|
||||
num = parseFloat(pin.entity.DefaultValue != ""
|
||||
? pin.entity.DefaultValue
|
||||
: pin.entity.AutogeneratedDefaultValue);
|
||||
}
|
||||
if (isNaN(num)) {
|
||||
num = 0;
|
||||
updateDefaultValue = false;
|
||||
}
|
||||
values[0] = Utility.minDecimals(num);
|
||||
super.setInputs(pin, values, updateDefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
|
||||
class StringPinTemplate extends IInputPinTemplate {
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
setup(pin) {
|
||||
super.setup(pin);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
@@ -3230,6 +3400,51 @@ class NamePinTemplate extends IInputPinTemplate {
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
|
||||
class RealPinTemplate extends IInputPinTemplate {
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
* @param {String[]?} values
|
||||
*/
|
||||
setInputs(pin, values = []) {
|
||||
let num = parseFloat(values.length ? values[0] : this.getInput(pin));
|
||||
let updateDefaultValue = true;
|
||||
if (isNaN(num)) {
|
||||
num = parseFloat(pin.entity.DefaultValue != ""
|
||||
? pin.entity.DefaultValue
|
||||
: pin.entity.AutogeneratedDefaultValue);
|
||||
}
|
||||
if (isNaN(num)) {
|
||||
num = 0;
|
||||
updateDefaultValue = false;
|
||||
}
|
||||
values[0] = Utility.minDecimals(num);
|
||||
super.setInputs(pin, values, updateDefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
|
||||
class StringPinTemplate extends IInputPinTemplate {
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
setup(pin) {
|
||||
super.setup(pin);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/GuidEntity").default} GuidEntity
|
||||
* @typedef {import("../entity/PinEntity").default} PinEntity
|
||||
@@ -3248,6 +3463,21 @@ class PinElement extends IElement {
|
||||
"name": NamePinTemplate,
|
||||
"real": RealPinTemplate,
|
||||
"string": StringPinTemplate,
|
||||
"struct": {
|
||||
"/Script/CoreUObject.LinearColor": ColorPinTemplate,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinEntity} pinEntity
|
||||
* @return {PinTemplate}
|
||||
*/
|
||||
static getTypeTemplate(pinEntity) {
|
||||
let result = PinElement.#typeTemplateMap[pinEntity.getType()];
|
||||
if (result.constructor === Object) {
|
||||
result = result[pinEntity.getSubCategory()];
|
||||
}
|
||||
return result ?? PinTemplate
|
||||
}
|
||||
|
||||
#color = ""
|
||||
@@ -3266,7 +3496,8 @@ class PinElement extends IElement {
|
||||
constructor(entity) {
|
||||
super(
|
||||
entity,
|
||||
new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)()
|
||||
// @ts-expect-error
|
||||
new (PinElement.getTypeTemplate(entity))()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3636,7 +3867,7 @@ class NodeElement extends ISelectableDraggableElement {
|
||||
*/
|
||||
static fromSerializedObject(str) {
|
||||
str = str.trim();
|
||||
let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
|
||||
let entity = SerializerFactory.getSerializer(ObjectEntity).deserialize(str);
|
||||
return new NodeElement(entity)
|
||||
}
|
||||
|
||||
@@ -4854,8 +5085,18 @@ customElements.define("ueb-blueprint", Blueprint);
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
class GeneralSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {new () => T} entityType
|
||||
*/
|
||||
constructor(wrap, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
|
||||
wrap = wrap ?? (v => `(${v})`);
|
||||
super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter);
|
||||
@@ -4863,7 +5104,6 @@ class GeneralSerializer extends ISerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {String} value
|
||||
* @returns {T}
|
||||
*/
|
||||
@@ -4877,27 +5117,42 @@ class GeneralSerializer extends ISerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
write(object) {
|
||||
let result = this.wrap(this.subWrite([], object));
|
||||
write(object, insideString = false) {
|
||||
let result = this.wrap(this.subWrite([], object, insideString));
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
class CustomSerializer extends GeneralSerializer {
|
||||
|
||||
/**
|
||||
* @param {new () => T} entityType
|
||||
*/
|
||||
constructor(objectWriter, entityType) {
|
||||
super(undefined, entityType);
|
||||
this.objectWriter = objectWriter;
|
||||
}
|
||||
|
||||
write(object) {
|
||||
let result = this.objectWriter(object);
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
write(object, insideString = false) {
|
||||
let result = this.objectWriter(object, insideString);
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -4910,25 +5165,45 @@ class PinSerializer extends GeneralSerializer {
|
||||
super(v => `${PinEntity.lookbehind} (${v})`, PinEntity, "", ",", true);
|
||||
}
|
||||
|
||||
writeValue(value, fullKey) {
|
||||
if (value?.constructor === String && fullKey == "DefaultValue") {
|
||||
/**
|
||||
* @param {String[]} fullKey
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
writeValue(value, fullKey, insideString) {
|
||||
if (value?.constructor === String && fullKey.length == 1 && fullKey[0] == "DefaultValue") {
|
||||
// @ts-expect-error
|
||||
return `"${Utility.encodeInputString(value)}"`
|
||||
}
|
||||
return super.writeValue(value, fullKey)
|
||||
return super.writeValue(value, fullKey, insideString)
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
class ToStringSerializer extends GeneralSerializer {
|
||||
|
||||
/**
|
||||
* @param {new () => T} entityType
|
||||
*/
|
||||
constructor(entityType) {
|
||||
super(undefined, entityType);
|
||||
}
|
||||
|
||||
write(object) {
|
||||
let result = object.toString();
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
write(object, insideString) {
|
||||
let result = insideString || object.isShownAsString()
|
||||
? `"${object.toString().replaceAll('"', '\\"')}"`
|
||||
: object.toString();
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -4937,6 +5212,13 @@ class ToStringSerializer extends GeneralSerializer {
|
||||
|
||||
function initializeSerializerFactory() {
|
||||
|
||||
const bracketsWrapped = v => `(${v})`;
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
LinearColorEntity,
|
||||
new GeneralSerializer(bracketsWrapped, LinearColorEntity)
|
||||
);
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
ObjectEntity,
|
||||
new ObjectSerializer()
|
||||
@@ -4949,12 +5231,12 @@ function initializeSerializerFactory() {
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
FunctionReferenceEntity,
|
||||
new GeneralSerializer(v => `(${v})`, FunctionReferenceEntity, "", ",", false)
|
||||
new GeneralSerializer(bracketsWrapped, FunctionReferenceEntity)
|
||||
);
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
KeyBindingEntity,
|
||||
new GeneralSerializer(v => `(${v})`, KeyBindingEntity, "", ",", false)
|
||||
new GeneralSerializer(bracketsWrapped, KeyBindingEntity)
|
||||
);
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
@@ -4980,7 +5262,9 @@ function initializeSerializerFactory() {
|
||||
objectReference.path
|
||||
? objectReference.type ? `'"${objectReference.path}"'` : `"${objectReference.path}"`
|
||||
: ""
|
||||
))
|
||||
),
|
||||
ObjectReferenceEntity
|
||||
)
|
||||
);
|
||||
|
||||
SerializerFactory.registerSerializer(IdentifierEntity, new ToStringSerializer(IdentifierEntity));
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
*/
|
||||
static fromSerializedObject(str) {
|
||||
str = str.trim()
|
||||
let entity = SerializerFactory.getSerializer(ObjectEntity).read(str)
|
||||
let entity = SerializerFactory.getSerializer(ObjectEntity).deserialize(str)
|
||||
return new NodeElement(entity)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
// @ts-check
|
||||
|
||||
import BoolPinTemplate from "../template/BoolPinTemplate"
|
||||
import ColorPinTemplate from "../template/ColorPinTemplate"
|
||||
import ExecPinTemplate from "../template/ExecPinTemplate"
|
||||
import IElement from "./IElement"
|
||||
import LinkElement from "./LinkElement"
|
||||
import NamePinTemplate from "../template/NamePinTemplate"
|
||||
import PinTemplate from "../template/PinTemplate"
|
||||
import RealPinTemplate from "../template/RealPinTemplate"
|
||||
import StringPinTemplate from "../template/StringPinTemplate"
|
||||
import Utility from "../Utility"
|
||||
import NamePinTemplate from "../template/NamePinTemplate"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/GuidEntity").default} GuidEntity
|
||||
@@ -28,6 +29,21 @@ export default class PinElement extends IElement {
|
||||
"name": NamePinTemplate,
|
||||
"real": RealPinTemplate,
|
||||
"string": StringPinTemplate,
|
||||
"struct": {
|
||||
"/Script/CoreUObject.LinearColor": ColorPinTemplate,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinEntity} pinEntity
|
||||
* @return {PinTemplate}
|
||||
*/
|
||||
static getTypeTemplate(pinEntity) {
|
||||
let result = PinElement.#typeTemplateMap[pinEntity.getType()]
|
||||
if (result.constructor === Object) {
|
||||
result = result[pinEntity.getSubCategory()]
|
||||
}
|
||||
return result ?? PinTemplate
|
||||
}
|
||||
|
||||
#color = ""
|
||||
@@ -46,7 +62,8 @@ export default class PinElement extends IElement {
|
||||
constructor(entity) {
|
||||
super(
|
||||
entity,
|
||||
new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)()
|
||||
// @ts-expect-error
|
||||
new (PinElement.getTypeTemplate(entity))()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// @ts-check
|
||||
|
||||
import ISerializable from "./ISerializable"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
|
||||
export default class IEntity {
|
||||
export default class IEntity extends ISerializable {
|
||||
|
||||
static attributes = {}
|
||||
|
||||
constructor(values) {
|
||||
super()
|
||||
/**
|
||||
* @param {Object} target
|
||||
* @param {Object} properties
|
||||
@@ -46,6 +48,7 @@ export default class IEntity {
|
||||
const value = Utility.objectGet(values, [property])
|
||||
if (value !== undefined) {
|
||||
target[property] = TypeInitialization.sanitize(value, defaultType)
|
||||
// We have a value, need nothing more
|
||||
continue
|
||||
}
|
||||
if (defaultValue instanceof TypeInitialization) {
|
||||
|
||||
15
js/entity/ISerializable.js
Normal file
15
js/entity/ISerializable.js
Normal file
@@ -0,0 +1,15 @@
|
||||
export default class ISerializable {
|
||||
|
||||
#showAsString = false
|
||||
|
||||
isShownAsString() {
|
||||
return this.#showAsString
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Boolean} v
|
||||
*/
|
||||
setShowAsString(v) {
|
||||
this.#showAsString = v
|
||||
}
|
||||
}
|
||||
21
js/entity/LinearColorEntity.js
Normal file
21
js/entity/LinearColorEntity.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// @ts-check
|
||||
|
||||
import IEntity from "./IEntity"
|
||||
|
||||
export default class LinearColorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
R: Number,
|
||||
G: Number,
|
||||
B: Number,
|
||||
A: Number,
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
/** @type {Number} */ this.R
|
||||
/** @type {Number} */ this.G
|
||||
/** @type {Number} */ this.B
|
||||
/** @type {Number} */ this.A
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
import GuidEntity from "./GuidEntity"
|
||||
import IEntity from "./IEntity"
|
||||
import LinearColorEntity from "./LinearColorEntity"
|
||||
import LocalizedTextEntity from "./LocalizedTextEntity"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity"
|
||||
import PinReferenceEntity from "./PinReferenceEntity"
|
||||
import SerializedType from "./SerializedType"
|
||||
import TypeInitialization from "./TypeInitialization"
|
||||
|
||||
export default class PinEntity extends IEntity {
|
||||
@@ -30,7 +32,7 @@ export default class PinEntity extends IEntity {
|
||||
bSerializeAsSinglePrecisionFloat: false,
|
||||
},
|
||||
LinkedTo: new TypeInitialization([PinReferenceEntity], false),
|
||||
DefaultValue: new TypeInitialization(String, false),
|
||||
DefaultValue: new TypeInitialization(new SerializedType(LinearColorEntity, String), false),
|
||||
AutogeneratedDefaultValue: new TypeInitialization(String, false),
|
||||
DefaultObject: new TypeInitialization(ObjectReferenceEntity, false, null),
|
||||
PersistentGuid: GuidEntity,
|
||||
@@ -140,4 +142,14 @@ export default class PinEntity extends IEntity {
|
||||
getType() {
|
||||
return this.PinType.PinCategory
|
||||
}
|
||||
|
||||
getSubCategory() {
|
||||
return this.PinType.PinSubCategoryObject.path
|
||||
}
|
||||
|
||||
getColorValue() {
|
||||
if (this.PinType.PinSubCategoryObject.path == "/Script/CoreUObject.LinearColor") {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
js/entity/SerializedType.js
Normal file
26
js/entity/SerializedType.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
* @typedef {(new (object?: Object) => IEntity) | StringConstructor | NumberConstructor | BooleanConstructor} Constructor
|
||||
* @typedef {Constructor|Constructor[]} AcceptedType
|
||||
*/
|
||||
|
||||
export default class SerializedType {
|
||||
|
||||
/** @type {(Constructor|Array<Constructor>)[]} */
|
||||
#types
|
||||
get types() {
|
||||
return this.#types
|
||||
}
|
||||
set types(v) {
|
||||
this.#types = v
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {...AcceptedType} acceptedTypes
|
||||
*/
|
||||
constructor(...acceptedTypes) {
|
||||
this.#types = acceptedTypes
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
import SerializedType from "./SerializedType"
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
@@ -36,20 +38,21 @@ export default class TypeInitialization {
|
||||
targetType = value?.constructor
|
||||
}
|
||||
let wrongType = false
|
||||
if (targetType && value?.constructor !== targetType && !(value instanceof targetType)) {
|
||||
wrongType = true
|
||||
if (
|
||||
targetType
|
||||
&& targetType !== SerializedType
|
||||
&& !(value?.constructor === targetType || value instanceof targetType)
|
||||
) {
|
||||
value = new targetType(value)
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
|
||||
value = value.valueOf() // Get the relative primitive value
|
||||
}
|
||||
if (wrongType) {
|
||||
return new targetType(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {(new () => T) | StringConstructor | NumberConstructor | BooleanConstructor} Constructor
|
||||
* @typedef {(new () => T) | SerializedType | StringConstructor | NumberConstructor | BooleanConstructor} Constructor
|
||||
* @param {Constructor|Array<Constructor>} type
|
||||
* @param {Boolean} showDefault
|
||||
* @param {any} value
|
||||
@@ -58,6 +61,8 @@ export default class TypeInitialization {
|
||||
if (value === undefined) {
|
||||
if (type instanceof Array) {
|
||||
value = []
|
||||
} else if (type instanceof SerializedType) {
|
||||
value = ""
|
||||
} else {
|
||||
value = TypeInitialization.sanitize(new type())
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default class Copy extends IInput {
|
||||
}
|
||||
|
||||
copied() {
|
||||
const value = this.blueprint.getNodes(true).map(node => this.serializer.write(node.entity)).join("\n")
|
||||
const value = this.blueprint.getNodes(true).map(node => this.serializer.serialize(node.entity, false)).join("\n")
|
||||
navigator.clipboard.writeText(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,30 @@
|
||||
|
||||
import GeneralSerializer from "./GeneralSerializer"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
export default class CustomSerializer extends GeneralSerializer {
|
||||
|
||||
/**
|
||||
* @param {new () => T} entityType
|
||||
*/
|
||||
constructor(objectWriter, entityType) {
|
||||
super(undefined, entityType)
|
||||
this.objectWriter = objectWriter
|
||||
}
|
||||
|
||||
write(object) {
|
||||
let result = this.objectWriter(object)
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
write(object, insideString = false) {
|
||||
let result = this.objectWriter(object, insideString)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,18 @@
|
||||
import Grammar from "./Grammar"
|
||||
import ISerializer from "./ISerializer"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
export default class GeneralSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {new () => T} entityType
|
||||
*/
|
||||
constructor(wrap, entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
|
||||
wrap = wrap ?? (v => `(${v})`)
|
||||
super(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter)
|
||||
@@ -12,7 +22,6 @@ export default class GeneralSerializer extends ISerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {String} value
|
||||
* @returns {T}
|
||||
*/
|
||||
@@ -26,12 +35,12 @@ export default class GeneralSerializer extends ISerializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
write(object) {
|
||||
let result = this.wrap(this.subWrite([], object))
|
||||
write(object, insideString = false) {
|
||||
let result = this.wrap(this.subWrite([], object, insideString))
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import IdentifierEntity from "../entity/IdentifierEntity"
|
||||
import IntegerEntity from "../entity/IntegerEntity"
|
||||
import InvariantTextEntity from "../entity/InvariantTextEntity"
|
||||
import KeyBindingEntity from "../entity/KeyBindingEntity"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity"
|
||||
import LocalizedTextEntity from "../entity/LocalizedTextEntity"
|
||||
import ObjectEntity from "../entity/ObjectEntity"
|
||||
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity"
|
||||
@@ -13,9 +14,14 @@ import Parsimmon from "parsimmon"
|
||||
import PathSymbolEntity from "../entity/PathSymbolEntity"
|
||||
import PinEntity from "../entity/PinEntity"
|
||||
import PinReferenceEntity from "../entity/PinReferenceEntity"
|
||||
import SerializedType from "../entity/SerializedType"
|
||||
import TypeInitialization from "../entity/TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
let P = Parsimmon
|
||||
|
||||
export default class Grammar {
|
||||
@@ -25,6 +31,27 @@ export default class Grammar {
|
||||
static getGrammarForType(r, attributeType, defaultGrammar) {
|
||||
if (attributeType instanceof TypeInitialization) {
|
||||
attributeType = attributeType.type
|
||||
return Grammar.getGrammarForType(r, attributeType, defaultGrammar)
|
||||
}
|
||||
if (attributeType instanceof SerializedType) {
|
||||
const noStringTypes = attributeType.types.filter(t => t !== String)
|
||||
let result = P.alt(
|
||||
...noStringTypes.map(t =>
|
||||
Grammar.getGrammarForType(r, t).wrap(P.string('"'), P.string('"')).map(
|
||||
/**
|
||||
* @param {IEntity} entity
|
||||
*/
|
||||
entity => {
|
||||
entity.setShowAsString(true) // Showing as string because it is inside a SerializedType
|
||||
return entity
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
if (noStringTypes.length < attributeType.types.length) {
|
||||
result = result.or(r.String) // Separated because it cannot be wrapped into " and "
|
||||
}
|
||||
return result
|
||||
}
|
||||
switch (Utility.getType(attributeType)) {
|
||||
case Boolean:
|
||||
@@ -47,6 +74,8 @@ export default class Grammar {
|
||||
return r.InvariantText
|
||||
case PinReferenceEntity:
|
||||
return r.PinReference
|
||||
case LinearColorEntity:
|
||||
return r.LinearColor
|
||||
case FunctionReferenceEntity:
|
||||
return r.FunctionReference
|
||||
case PinEntity:
|
||||
@@ -163,7 +192,7 @@ export default class Grammar {
|
||||
r.None,
|
||||
...[r.ReferencePath.map(path => new ObjectReferenceEntity({ type: "", path: path }))]
|
||||
.flatMap(referencePath => [
|
||||
referencePath, // version having just path
|
||||
referencePath, // Version having just path
|
||||
referencePath.trim(P.string('"')) // Version having path surround with double quotes
|
||||
]),
|
||||
P.seqMap(
|
||||
@@ -222,6 +251,8 @@ export default class Grammar {
|
||||
})
|
||||
)
|
||||
|
||||
LinearColor = r => Grammar.createMultiAttributeGrammar(r, LinearColorEntity)
|
||||
|
||||
FunctionReference = r => Grammar.createMultiAttributeGrammar(r, FunctionReferenceEntity)
|
||||
|
||||
KeyBinding = r => P.alt(
|
||||
|
||||
@@ -7,6 +7,9 @@ import SerializerFactory from "./SerializerFactory"
|
||||
import TypeInitialization from "../entity/TypeInitialization"
|
||||
import Utility from "../Utility"
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
export default class ISerializer {
|
||||
|
||||
static grammar = Parsimmon.createLanguage(new Grammar())
|
||||
@@ -20,21 +23,66 @@ export default class ISerializer {
|
||||
this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join("."))
|
||||
}
|
||||
|
||||
writeValue(value, fullKey = undefined) {
|
||||
/**
|
||||
* @param {String} value
|
||||
* @returns {T}
|
||||
*/
|
||||
deserialize(value) {
|
||||
return this.read(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
serialize(object, insideString) {
|
||||
insideString ||= object.isShownAsString()
|
||||
let result = this.write(object, insideString)
|
||||
if (object.isShownAsString()) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} value
|
||||
* @returns {T}
|
||||
*/
|
||||
read(value) {
|
||||
throw new Error("Not implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
write(object, insideString) {
|
||||
throw new Error("Not implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String[]} fullKey
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
writeValue(value, fullKey, insideString) {
|
||||
if (value === null) {
|
||||
return "()"
|
||||
}
|
||||
const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v)
|
||||
const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).serialize(v)
|
||||
// This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically)
|
||||
switch (value?.constructor) {
|
||||
case Function:
|
||||
return this.writeValue(value(), fullKey)
|
||||
return this.writeValue(value(), fullKey, insideString)
|
||||
case Boolean:
|
||||
return Utility.FirstCapital(value.toString())
|
||||
case Number:
|
||||
return value.toString()
|
||||
case String:
|
||||
return `"${Utility.encodeString(value)}"`
|
||||
return insideString
|
||||
? `\\"${Utility.encodeString(value)}\\"`
|
||||
: `"${Utility.encodeString(value)}"`
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
return `(${value.map(v => serialize(v) + ",").join("")})`
|
||||
@@ -47,25 +95,26 @@ export default class ISerializer {
|
||||
/**
|
||||
* @param {String[]} key
|
||||
* @param {Object} object
|
||||
* @param {Boolean} insideString
|
||||
* @returns {String}
|
||||
*/
|
||||
subWrite(key, object) {
|
||||
subWrite(key, object, insideString) {
|
||||
let result = ""
|
||||
let fullKey = key.concat("")
|
||||
const last = fullKey.length - 1
|
||||
for (const property of Object.getOwnPropertyNames(object)) {
|
||||
fullKey[last] = property
|
||||
const value = object[property]
|
||||
if (object[property]?.constructor === Object) {
|
||||
if (value?.constructor === Object) {
|
||||
// Recursive call when finding an object
|
||||
result += (result.length ? this.separator : "")
|
||||
+ this.subWrite(fullKey, value)
|
||||
+ this.subWrite(fullKey, value, insideString)
|
||||
} else if (value !== undefined && this.showProperty(object, fullKey, value)) {
|
||||
result += (result.length ? this.separator : "")
|
||||
+ this.prefix
|
||||
+ this.attributeKeyPrinter(fullKey)
|
||||
+ this.attributeValueConjunctionSign
|
||||
+ this.writeValue(value, fullKey)
|
||||
+ this.writeValue(value, fullKey, insideString)
|
||||
}
|
||||
}
|
||||
if (this.trailingSeparator && result.length && fullKey.length === 1) {
|
||||
|
||||
@@ -22,6 +22,9 @@ export default class ObjectSerializer extends ISerializer {
|
||||
return super.showProperty(object, attributeKey, attributeValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} value
|
||||
*/
|
||||
read(value) {
|
||||
const parseResult = ISerializer.grammar.Object.parse(value)
|
||||
if (!parseResult.status) {
|
||||
@@ -32,7 +35,6 @@ export default class ObjectSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {String} value
|
||||
* @returns {ObjectEntity[]}
|
||||
*/
|
||||
readMultiple(value) {
|
||||
const parseResult = ISerializer.grammar.MultipleObject.parse(value)
|
||||
@@ -44,16 +46,17 @@ export default class ObjectSerializer extends ISerializer {
|
||||
|
||||
/**
|
||||
* @param {ObjectEntity} object
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
write(object) {
|
||||
let result = `Begin Object Class=${object.Class.path} Name=${this.writeValue(object.Name, "Name")}
|
||||
${this.subWrite([], object)
|
||||
write(object, insideString) {
|
||||
let result = `Begin Object Class=${object.Class.path} Name=${this.writeValue(object.Name, ["Name"], insideString)}
|
||||
${this.subWrite([], object, insideString)
|
||||
+ object
|
||||
.CustomProperties.map(pin =>
|
||||
this.separator
|
||||
+ this.prefix
|
||||
+ "CustomProperties "
|
||||
+ SerializerFactory.getSerializer(PinEntity).write(pin)
|
||||
+ SerializerFactory.getSerializer(PinEntity).serialize(pin)
|
||||
)
|
||||
.join("")}
|
||||
End Object\n`
|
||||
|
||||
@@ -10,11 +10,15 @@ export default class PinSerializer extends GeneralSerializer {
|
||||
super(v => `${PinEntity.lookbehind} (${v})`, PinEntity, "", ",", true)
|
||||
}
|
||||
|
||||
writeValue(value, fullKey) {
|
||||
if (value?.constructor === String && fullKey == "DefaultValue") {
|
||||
/**
|
||||
* @param {String[]} fullKey
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
writeValue(value, fullKey, insideString) {
|
||||
if (value?.constructor === String && fullKey.length == 1 && fullKey[0] == "DefaultValue") {
|
||||
// @ts-expect-error
|
||||
return `"${Utility.encodeInputString(value)}"`
|
||||
}
|
||||
return super.writeValue(value, fullKey)
|
||||
return super.writeValue(value, fullKey, insideString)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,30 @@
|
||||
|
||||
import GeneralSerializer from "./GeneralSerializer"
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
*/
|
||||
export default class ToStringSerializer extends GeneralSerializer {
|
||||
|
||||
/**
|
||||
* @param {new () => T} entityType
|
||||
*/
|
||||
constructor(entityType) {
|
||||
super(undefined, entityType)
|
||||
}
|
||||
|
||||
write(object) {
|
||||
let result = object.toString()
|
||||
/**
|
||||
* @param {T} object
|
||||
* @param {Boolean} insideString
|
||||
*/
|
||||
write(object, insideString) {
|
||||
let result = insideString || object.isShownAsString()
|
||||
? `"${object.toString().replaceAll('"', '\\"')}"`
|
||||
: object.toString()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import IdentifierEntity from "../entity/IdentifierEntity"
|
||||
import IntegerEntity from "../entity/IntegerEntity"
|
||||
import InvariantTextEntity from "../entity/InvariantTextEntity"
|
||||
import KeyBindingEntity from "../entity/KeyBindingEntity"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity"
|
||||
import LocalizedTextEntity from "../entity/LocalizedTextEntity"
|
||||
import ObjectEntity from "../entity/ObjectEntity"
|
||||
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity"
|
||||
@@ -21,6 +22,13 @@ import ToStringSerializer from "./ToStringSerializer"
|
||||
|
||||
export default function initializeSerializerFactory() {
|
||||
|
||||
const bracketsWrapped = v => `(${v})`
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
LinearColorEntity,
|
||||
new GeneralSerializer(bracketsWrapped, LinearColorEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
ObjectEntity,
|
||||
new ObjectSerializer()
|
||||
@@ -33,12 +41,12 @@ export default function initializeSerializerFactory() {
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
FunctionReferenceEntity,
|
||||
new GeneralSerializer(v => `(${v})`, FunctionReferenceEntity, "", ",", false)
|
||||
new GeneralSerializer(bracketsWrapped, FunctionReferenceEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
KeyBindingEntity,
|
||||
new GeneralSerializer(v => `(${v})`, KeyBindingEntity, "", ",", false)
|
||||
new GeneralSerializer(bracketsWrapped, KeyBindingEntity)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
@@ -64,7 +72,9 @@ export default function initializeSerializerFactory() {
|
||||
objectReference.path
|
||||
? objectReference.type ? `'"${objectReference.path}"'` : `"${objectReference.path}"`
|
||||
: ""
|
||||
))
|
||||
),
|
||||
ObjectReferenceEntity
|
||||
)
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(IdentifierEntity, new ToStringSerializer(IdentifierEntity))
|
||||
|
||||
61
js/template/ColorPinTemplate.js
Normal file
61
js/template/ColorPinTemplate.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// @ts-check
|
||||
|
||||
import html from "./html"
|
||||
import IInputPinTemplate from "./IInputPinTemplate"
|
||||
|
||||
/**
|
||||
* @typedef {import("../element/PinElement").default} PinElement
|
||||
*/
|
||||
|
||||
export default class ColorPinTemplate extends IInputPinTemplate {
|
||||
|
||||
/** @type {HTMLInputElement} */
|
||||
#input
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
setup(pin) {
|
||||
super.setup(pin)
|
||||
this.#input = pin.querySelector(".ueb-pin-input")
|
||||
let self = this
|
||||
this.onChangeHandler = _ => pin.entity.DefaultValue = self.#input.checked ? "true" : "false"
|
||||
this.#input.addEventListener("change", this.onChangeHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
cleanup(pin) {
|
||||
super.cleanup(pin)
|
||||
this.#input.removeEventListener("change", this.onChangeHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
getInputs(pin) {
|
||||
return [this.#input.checked ? "true" : "false"]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
* @param {String[]?} value
|
||||
*/
|
||||
setInputs(pin, value = []) {
|
||||
pin.entity.DefaultValue = value.length ? value[0] : this.getInput(pin)
|
||||
this.#input.checked = pin.entity.DefaultValue == "true"
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
*/
|
||||
renderInput(pin) {
|
||||
if (pin.isInput()) {
|
||||
return html`
|
||||
<span class="ueb-pin-input" ${pin.entity.DefaultValue == "true" ? "checked" : ""}></span>
|
||||
`
|
||||
}
|
||||
return super.renderInput(pin)
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
"terser": "^5.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@easylogic/colorpicker": "^1.10.11",
|
||||
"parsimmon": "^1.18.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user