in pxtblocks/blocklyloader.ts [665:854]
function buildBlockFromDef(def: pxtc.ParsedBlockDef, expanded = false) {
let anonIndex = 0;
let firstParam = !expanded && !!comp.thisParameter;
const inputs = splitInputs(def);
const imgConv = new ImageConverter()
if (fn.attributes.shim === "ENUM_GET" || fn.attributes.shim === "KIND_GET") {
if (comp.parameters.length > 1 || comp.thisParameter) {
console.warn(`Enum blocks may only have 1 parameter but ${fn.attributes.blockId} has ${comp.parameters.length}`);
return;
}
}
inputs.forEach(inputParts => {
const fields: NamedField[] = [];
let inputName: string;
let inputCheck: string | string[];
let hasParameter = false;
inputParts.forEach(part => {
if (part.kind !== "param") {
const f = newLabel(part);
if (f) {
fields.push({ field: f });
}
}
else if (fn.attributes.shim === "ENUM_GET") {
U.assert(!!fn.attributes.enumName, "Trying to create an ENUM_GET block without a valid enum name")
fields.push({
name: "MEMBER",
field: new pxtblockly.FieldUserEnum(info.enumsByName[fn.attributes.enumName])
});
return;
}
else if (fn.attributes.shim === "KIND_GET") {
fields.push({
name: "MEMBER",
field: new pxtblockly.FieldKind(info.kindsByName[fn.attributes.kindNamespace || fn.attributes.blockNamespace || fn.namespace])
});
return;
}
else {
// find argument
let pr = getParameterFromDef(part, comp, firstParam);
firstParam = false;
if (!pr) {
console.error("block " + fn.attributes.blockId + ": unknown parameter " + part.name + (part.ref ? ` (${part.ref})` : ""));
return;
}
if (isHandlerArg(pr)) {
inputName = "HANDLER_DRAG_PARAM_" + pr.name;
inputCheck = fn.attributes.draggableParameters === "reporter" ? getBlocklyCheckForType(pr.type, info) : "Variable";
return;
}
let typeInfo = U.lookup(info.apis.byQName, pr.type)
hasParameter = true;
const defName = pr.definitionName;
const actName = pr.actualName;
let isEnum = typeInfo && typeInfo.kind == pxtc.SymbolKind.Enum
let isFixed = typeInfo && !!typeInfo.attributes.fixedInstances && !pr.shadowBlockId;
let isConstantShim = !!fn.attributes.constantShim;
let isCombined = pr.type == "@combined@"
let customField = pr.fieldEditor;
let fieldLabel = defName.charAt(0).toUpperCase() + defName.slice(1);
let fieldType = pr.type;
if (isEnum || isFixed || isConstantShim || isCombined) {
let syms: pxtc.SymbolInfo[];
if (isEnum) {
syms = getEnumDropdownValues(info.apis, pr.type);
}
else if (isFixed) {
syms = getFixedInstanceDropdownValues(info.apis, typeInfo.qName);
}
else if (isCombined) {
syms = fn.combinedProperties.map(p => U.lookup(info.apis.byQName, p))
}
else {
syms = getConstantDropdownValues(info.apis, fn.qName);
}
if (syms.length == 0) {
console.error(`no instances of ${typeInfo.qName} found`)
}
const dd = syms.map(v => {
let k = v.attributes.block || v.attributes.blockId || v.name;
let comb = v.attributes.blockCombine
if (v.attributes.jresURL && !v.attributes.iconURL && U.startsWith(v.attributes.jresURL, "data:image/x-mkcd-f")) {
v.attributes.iconURL = imgConv.convert(v.attributes.jresURL)
}
if (!!comb)
k = k.replace(/@set/, "")
return [
v.attributes.iconURL || v.attributes.blockImage ? {
src: v.attributes.iconURL || Util.pathJoin(pxt.webConfig.commitCdnUrl, `blocks/${v.namespace.toLowerCase()}/${v.name.toLowerCase()}.png`),
alt: k,
width: 36,
height: 36,
value: v.name
} : k,
v.namespace + "." + v.name
];
});
// if a value is provided, move it first
if (pr.defaultValue) {
let shadowValueIndex = -1;
dd.some((v, i) => {
if (v[1] === (pr as BlockParameter).defaultValue) {
shadowValueIndex = i;
return true;
}
return false;
});
if (shadowValueIndex > -1) {
const shadowValue = dd.splice(shadowValueIndex, 1)[0];
dd.unshift(shadowValue);
}
}
if (customField) {
let defl = fn.attributes.paramDefl[actName] || "";
const options = {
data: dd,
colour: color,
label: fieldLabel,
type: fieldType,
blocksInfo: info
} as Blockly.FieldCustomDropdownOptions;
Util.jsonMergeFrom(options, fn.attributes.paramFieldEditorOptions && fn.attributes.paramFieldEditorOptions[actName] || {});
fields.push(namedField(createFieldEditor(customField, defl, options), defName));
}
else
fields.push(namedField(new Blockly.FieldDropdown(dd), defName));
} else if (customField) {
const defl = fn.attributes.paramDefl[pr.actualName] || "";
const options = {
colour: color,
label: fieldLabel,
type: fieldType,
blocksInfo: info
} as Blockly.FieldCustomOptions;
Util.jsonMergeFrom(options, fn.attributes.paramFieldEditorOptions && fn.attributes.paramFieldEditorOptions[pr.actualName] || {});
fields.push(namedField(createFieldEditor(customField, defl, options), pr.definitionName));
} else {
inputName = defName;
if (instance && part.name === "this") {
inputCheck = pr.type;
} else if (pr.type == "number" && pr.shadowBlockId && pr.shadowBlockId == "value") {
inputName = undefined;
fields.push(namedField(new Blockly.FieldNumber("0"), defName));
} else if (pr.type == "string" && pr.shadowOptions && pr.shadowOptions.toString) {
inputCheck = null;
} else {
inputCheck = getBlocklyCheckForType(pr.type, info);
}
}
}
});
let input: Blockly.Input;
if (inputName) {
input = block.appendValueInput(inputName);
input.setAlign(Blockly.ALIGN_LEFT);
}
else if (expanded) {
const prefix = hasParameter ? optionalInputWithFieldPrefix : optionalDummyInputPrefix;
input = block.appendDummyInput(prefix + (anonIndex++));
}
else {
input = block.appendDummyInput();
}
if (inputCheck) {
input.setCheck(inputCheck);
}
fields.forEach(f => input.appendField(f.field, f.name));
});
imgConv.logTime()
}