Hi Maribeth,
thanks a lot for your very detailed and helpful answer!
I had indeed noticed the different style after changing to field_number but I couldn't recall these basic details as I had not worked with Blockly for a long time. Your explanations made it clear! Also thanks for pointing to the different renderers!
Also thanks for explaining the limits (or potential pitfalls) of input validation. It's really only meant as a first level of validation for the user. The NQC compiler and the LEGO RCX firmware will have additional checks (and the code generation could add additional checks). It's clear that expressions more complex than having a simple number require more complex validation - this is where I don't want to dig deeper and keep the flexibility. It's OK to let the compiler and/or firmware handle those more complex errors in these cases.
Pointing me to the onChange event listener was exactly, what I was missing and am using now.
A generic helper function that I have added. It may contain some redundant checks though. Maybe others find it useful, so I post it here.
(I'll also add a question about this approach below.)
```
function checkIntegerInputRange(block, inputName, minValue, maxValue) {
let warnText = "";
let targetBlock = block.getInput(inputName).connection.targetBlock();
if (targetBlock) {
let inputList = targetBlock.inputList;
if (Array.isArray(inputList) && (inputList.length == 1)) {
if (inputList[0].type == Blockly.inputTypes.DUMMY) {
let fieldRow = inputList[0].fieldRow;
if(Array.isArray(fieldRow) && (fieldRow.length == 1) && (fieldRow[0].name == "NUM")) {
let value = fieldRow[0].getValue();
if (!Number.isInteger(value)) {
warnText += 'Accepting only integer numbers: rounded last input. ';
value = Math.round(value);
}
if (value < minValue) {
warnText += 'Accepting only numbers up to ' + minValue + '. ';
value = minValue;
} else if(value > maxValue) {
warnText += 'Accepting only numbers below ' + maxValue + '. ';
value = maxValue;
}
fieldRow[0].setValue(value);
}
}
}
}
block.setWarningText(warnText); // empty warning text is not displayed, which makes it a nice default
}
```
Having the core functionally in a function, this allows me to use it in multiple blocks using the second mechanism: extensions.
```
{
"type": "output_setpower",
"message0": "set output %1 power level to %2",
"args0": [
...
{
"type": "input_value",
"name": "POWER",
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"colour": outputsCategoryCol,
"tooltip": "",
"helpUrl": "",
"extensions": [
'power_range_validation',
],
},
(...)
Blockly.Extensions.register('message_range_validation', function() {
this.setOnChange(function(changeEvent) {
if (changeEvent instanceof Blockly.Events.BlockChange) {
checkIntegerInputRange(this, 'MESSAGE', 0, 255);
}
});
});
```
+
```
{
"type": "infrared_msgsend",
"message0": "send IR message %1 via IR",
"args0": [
{
"type": "input_value",
"name": "MESSAGE",
"check": "Number"
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"colour": infraredCategoryCol,
"tooltip": "",
"helpUrl": "",
"extensions": [
'message_range_validation',
],
},
(...)
Blockly.Extensions.register('power_range_validation', function() {
this.setOnChange(function(changeEvent) {
if (changeEvent instanceof Blockly.Events.BlockChange) {
checkIntegerInputRange(this, 'POWER', 0, 7);
}
});
});
```
Overall, this works quite nicely. However, there's one final question:
`checkIntegerInputRange()` builds a warning message and performs automatic corrections, i.e.
- rounding floats to integers (e.g. 3.2 to 3 or 5.7 to 6)
- range checking (setting -4 to lower limit of 0 and 9 to upper limit of 7).
And even a combination of both, i.e. 12.4 also becomes 7.
To inform the user about these automatic corrections, I want to display them to the user as well by calling `block.setWarningText()`.
The "problem" is that calling `setValue()` with the auto-corrected value will not only set the value but trigger another onchange event with a valid value which leads to the removal of the warning message. Is there a way to work around this? Manipulating the protected value field directly instead of calling a setter method? Feels wrong and I don't know if this would even work.
Or do I have to choose between "warn and let the user correct their input themselves" and "auto-correct but do not show a warning about what happened"?
I also feared that calling.`setValue()` leads to an infinite number of calls ("recursion"), but luckily overwriting with the same value seems not to cause a change event (makes sense).
Again, thanks a lot for your help!
Regards
Mäh