I need help upgrading custom fields to the newest version of Blockly

190 views
Skip to first unread message

Matthew Matz

unread,
Jul 8, 2023, 11:30:43 PM7/8/23
to Blockly
Good evening,

We are using a version of blockly from early 2020 on our website, and for numerous reasons we need to upgrade it, but switching to npm is proving to be very challenging.  I am able to get a lot of things working now, but I am struggling with the two custom fields we use.  I am hoping that if I can get help with one that I can figure out how to upgrade the other.

When the field is working, it looks like this, and provides a text value that represents the pitch for a musical note:
Screen Shot 2023-07-08 at 8.28.26 PM.png

Here is my code for this:
/**
* @license
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Music pitch input field. Borrowed from Blockly Games.
* @author fraser@google.com (Neil Fraser)
* @author samelh@google.com (Sam El-Husseini)
* @author matt.m.matz@gmail.com (Matthew Matz) - Modified origial example
*/


var Blockly = require("blockly");


class FieldPitch extends Blockly.FieldTextInput {
constructor(text) {
super(text);

// ... existing constructor code ...

/**
* Click event data.
* @type {?Blockly.EventData}
* @private
*/
this.clickWrapper_ = null;

/**
* Move event data.
* @type {?Blockly.EventData}
* @private
*/
this.moveWrapper_ = null;

this.SERIALIZABLE = true;
}
}

/**
* Construct a FieldPitch from a JSON arg object.
* @param {!Object} options A JSON object with options (pitch).
* @return {!Blockly.FieldPitch} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldPitch.fromJson = function (options) {
return new Blockly.FieldPitch(options['pitch']);
};



/**
* A searchable string containing normalized names for each note.
* Dividing .indexOf() by 6 and removing the decimal correlates
* to the indicies of Blockly.FieldPitch.TONES and
* Blockly.FieldPitch.NOTES
*/
Blockly.FieldPitch.SEARCH_STRING = 'Bb1As1B1----C2----Db2Cs2D2----Eb2Ds2E2----F2----Gb2Fs2G2----Ab2Gs2A2----Bb2As2B2----C3----Db3Cs3D3----Eb3Ds3E3----F3----Gb3Fs3G3----Ab3Gs3A3----Bb3As3B3----C4----Db4Cs4D4----Eb4Ds4E4----F4----Gb4Fs4G4----Ab4Gs4A4----Bb4As4B4----C5----Db5Cs5D5----Eb5Ds5E5----F5----Gb5Fs5G5----Ab5Gs5A5----Bb5As5B5----C6----Db6Cs6D6----Eb6Ds6E6----F6----Gb6Fs6G6----Ab6Gs6A6----Bb6As6B6----';

/**
* The frequencies of all notes available for the picker. This is
* the value output by the field.
*/
Blockly.FieldPitch.TONES = [
51.915,
55, 65.41, 69.3, 73.42, 77.78, 82.41, 87.31, 92.5, 98, 103.83,
110, 116.54, 123.47, 130.82, 138.6, 146.84, 155.56, 164.82, 174.62, 185, 196, 207.66,
220, 233.08, 246.94, 261.64, 277.2, 293.68, 311.12, 329.64, 349.24, 370, 392, 415.32,
440, 466.16, 493.88, 523.28, 554.4, 587.36, 622.24, 659.28, 698.48, 740, 784, 830.64,
880, 932.32, 987.76, 1046.56, 1108.8, 1174.72, 1244.48, 1318.56, 1396.96, 1480, 1568, 1661.28,
1760, 1864.64, 1975.52
];


/**
* Pretty-text representation of all notes available for the picker.
*/
Blockly.FieldPitch.NOTES = [
'B\u{266d}1', ' B1 ',
' C2 ', 'D\u{266d}2', ' D2 ', 'E\u{266d}2', ' E2 ', ' F2 ', 'G\u{266d}2', ' G2 ', 'A\u{266d}2', ' A2 ', 'B\u{266d}2', ' B2 ',
' C3 ', 'D\u{266d}3', ' D3 ', 'E\u{266d}3', ' E3 ', ' F3 ', 'G\u{266d}3', ' G3 ', 'A\u{266d}3', ' A3 ', 'B\u{266d}3', ' B3 ',
' C4 ', 'D\u{266d}4', ' D4 ', 'E\u{266d}4', ' E4 ', ' F4 ', 'G\u{266d}4', ' G4 ', 'A\u{266d}4', ' A4 ', 'B\u{266d}4', ' B4 ',
' C5 ', 'D\u{266d}5', ' D5 ', 'E\u{266d}5', ' E5 ', ' F5 ', 'G\u{266d}5', ' G5 ', 'A\u{266d}5', ' A5 ', 'B\u{266d}5', ' B5 ',
' C6 ', 'D\u{266d}6', ' D6 ', 'E\u{266d}6', ' E6 ', ' F6 ', 'G\u{266d}6', ' G6 ', 'A\u{266d}6', ' A6 '
];


/**
* Show the inline free-text editor on top of the text and the note picker.
* @protected
*/
Blockly.FieldPitch.prototype.showEditor_ = function () {
Blockly.FieldPitch.superClass_.showEditor_.call(this);

var div = Blockly.WidgetDiv.DIV;
if (!div.firstChild) {
// Mobile interface uses Blockly.prompt.
return;
}
// Build the DOM.
var editor = this.dropdownCreate_();
Blockly.DropDownDiv.getContentDiv().appendChild(editor);

Blockly.DropDownDiv.setColour(this.sourceBlock_.style.colourPrimary,
this.sourceBlock_.style.colourTertiary);

Blockly.DropDownDiv.showPositionedByField(
this, this.dropdownDispose_.bind(this));

// The note picker is different from other fields in that it updates on
// mousemove even if it's not in the middle of a drag. In future we may
// change this behaviour. For now, using bindEvent_ instead of
// bindEventWithChecks_ allows it to work without a mousedown/touchstart.
this.clickWrapper_ =
Blockly.bindEvent_(this.containerElement_, 'click', this,
this.hide_);
this.moveWrapper_ =
Blockly.bindEvent_(this.containerElement_, 'mousemove', this,
this.onMouseMove);

this.updateGraph_();
};

/**
* Create the pitch editor.
* @return {!Element} The newly created pitch picker.
* @private
*/
Blockly.FieldPitch.prototype.dropdownCreate_ = function () {
this.containerElement_ = document.createElement('div');
this.containerElement_.className = 'musicScales';
this.imageElement_ = document.createElement('div');
this.imageElement_.className = 'notePicker';

this.containerElement_.appendChild(this.imageElement_);

return this.containerElement_;
};

/**
* Dispose of events belonging to the pitch editor.
* @private
*/
Blockly.FieldPitch.prototype.dropdownDispose_ = function () {
if (this.clickWrapper_) {
Blockly.unbindEvent_(this.clickWrapper_);
this.clickWrapper_ = null;
}
if (this.moveWrapper_) {
Blockly.unbindEvent_(this.moveWrapper_);
this.moveWrapper_ = null;
}
this.imageElement_ = null;
this.containerElement_ = null;
};

/**
* Hide the editor.
* @private
*/
Blockly.FieldPitch.prototype.hide_ = function () {
Blockly.WidgetDiv.hide();
Blockly.DropDownDiv.hideWithoutAnimation();
};

/**
* Set the note to match the mouse's position.
* @param {!Event} e Mouse move event.
*/
Blockly.FieldPitch.prototype.onMouseMove = function (e) {
var i = this.getValue();
var bBox = this.imageElement_.getBoundingClientRect();
var dy = e.clientY - bBox.top;
var note = Blockly.utils.math.clamp(Math.round(58 - (dy - 26) / 4.4), 0, 59);
this.imageElement_.style.backgroundPosition = Math.floor(-note * 17700 / 571) + 'px 0px';
var j = Blockly.FieldPitch.TONES[note];
this.setEditorValue_(j);
if (i !== j && playSound) {
playSound("instrument|piano|0.1|" + j);
}
};

/**
* Convert the machine-readable value (0-12) to human-readable text (C3-A4).
* @param {number|string} value The provided value.
* @return {string|undefined} The respective note, or undefined if invalid.
*/
Blockly.FieldPitch.prototype.valueToNote = function (value) {
var i = Blockly.FieldPitch.TONES.findClosestIndex(Number(value));
return Blockly.FieldPitch.NOTES[i];
};

/**
* Convert the human-readable text (C3-A4) to machine-readable value (0-12).
* @param {string} text The provided note.
* @return {number|undefined} The respective value, or undefined if invalid.
*/
Blockly.FieldPitch.prototype.noteToValue = function (text) {
var normalizedText = text.trim().toLowerCase().replace(/ /g, '').replace(/#/g, 's').split('');
if (normalizedText[0]) {
normalizedText[0] = normalizedText[0].toUpperCase();
} else {
normalizedText[0] = '';
}
var i = Blockly.FieldPitch.SEARCH_STRING.indexOf(normalizedText.join(''));
if (i < 0) {
return undefined;
} else {
i = Math.floor(i / 6);
return Blockly.FieldPitch.TONES[i];
}
};

/**
* Get the text to be displayed on the field node.
* @return {?string} The HTML value if we're editing, otherwise null. Null means
* the super class will handle it, likely a string cast of value.
* @protected
*/
Blockly.FieldPitch.prototype.getText_ = function () {
if (this.isBeingEdited_) {
return Blockly.FieldPitch.superClass_.getText_.call(this);
}
return this.valueToNote(this.getValue()) || null;
};

/**
* Transform the provided value into a text to show in the HTML input.
* @param {*} value The value stored in this field.
* @return {string} The text to show on the HTML input.
*/
Blockly.FieldPitch.prototype.getEditorText_ = function (value) {
return this.valueToNote(value);
};

/**
* Transform the text received from the HTML input (note) into a value
* to store in this field.
* @param {string} text Text received from the HTML input.
* @return {*} The value to store.
*/
Blockly.FieldPitch.prototype.getValueFromEditorText_ = function (text) {
return this.noteToValue(text);
};

/**
* Updates the graph when the field rerenders.
* @private
* @override
*/
Blockly.FieldPitch.prototype.render_ = function () {
Blockly.FieldPitch.superClass_.render_.call(this);
this.updateGraph_();
};

/**
* Redraw the note picker with the current note.
* @private
*/
Blockly.FieldPitch.prototype.updateGraph_ = function () {
if (!this.imageElement_) {
return;
}
var value = this.getValue();
var i = Blockly.FieldPitch.TONES.findClosestIndex(value);
this.imageElement_.style.backgroundPosition = (-i * 17700 / 571) + 'px 0';
};

/**
* Ensure that only a valid value may be entered.
* @param {*} opt_newValue The input value.
* @return {*} A valid value, or null if invalid.
*/
Blockly.FieldPitch.prototype.doClassValidation_ = function (opt_newValue) {
if (opt_newValue === null || opt_newValue === undefined) {
return null;
}
var note = this.valueToNote(opt_newValue);
if (note) {
return opt_newValue;
}
return null;
};

Blockly.fieldRegistry.register('field_pitch', Blockly.FieldPitch);

module.exports = FieldPitch;


And here are the relevant CSS classes that provide the background images:
.musicScales {
width: 90px;
height: 290px;
background-color: #fff;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='290' viewBox='647 -19 74 252'%3E%3Cpath fill='%23000' d='M651 174c-0-0 2-2 6-5 10-8 15-16 16-26-0-7-3-11-8-11-4 0-8 3-9 5-1 2 1 2 3 2 8-0 7 9 0 9-6-0-8-5-6-10 3-6 10-8 16-7 7 1 11 7 11 14-0 10-10 18-16 23-6 4-12 7-13 7zm31-25c0-5 6-5 7-0 0 5-6 5-7 0zm-0-12c-0-5 6-5 6 0 0 5-6 5-6 0zM663 121c-2-2-2-6 0-9 3-3 8-2 9 2 1 4 0 7-5 7 3 2 7 2 9 1 7-4 3-12 2-19-10 3-20-3-23-13-3-10 3-20 14-28-1-4-2-10-2-15 1-7 5-15 8-14 3 1 5 9 6 13 0 6-0 11-3 15-2 3-5 6-6 7 1 3 1 7 2 9 16-3 20 21 6 25 1 4 3 11 3 14-1 3-3 7-9 8-6 1-8-1-10-4zm14-20c-1-6-2-11-4-18-3 1-6 4-6 7-0 3 3 6 5 6 1 0-3 2-6-2-3-4-5-12 5-17-1-5-1-6-2-8-6 6-12 12-12 19-0 9 8 17 19 14zm-2-18c1 6 3 12 4 17 10-5 5-18-4-17zm-5-23c1-1 3-3 4-4 3-3 7-13 2-15-4-1-7 8-7 14 0 2 1 4 1 6z'/%3E%3Cpath d='M648 77h74M648 103h74M648 90h74M648 64h74M648 51h74M648 155h74M648 181h74M648 168h74M648 142h74M648 129h74' stroke='%23000'/%3E%3C/svg%3E");
background-size: cover;
background-position: 0px 6px;
}

.notePicker {
position: absolute;
top: 3px;
left: 60px;
height: 290px;
width: 30px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='1860' height='290' viewBox='727 100 1587 1'%3E%3Cdefs%3E%3Cpath id='n' d='m0 0a5 7 73 0 1-5 7 5 7 73 0 1-9-2 5 7 73 0 1 4-7 5 7 73 0 1 9 1'/%3E%3Cpath id='f' d='m0 0a5 7 73 0 1-5 7 5 7 73 0 1-9-2 5 7 73 0 1 4-7 5 7 73 0 1 9 1m6-2c0 1-0 1-1 2-1 1-1 1-2 2v-3c0-1 1-1 2-1 1 0 1 1 1 1zm-0-2c-1 0-2 1-3 1v-7h-1v12c0 0 0 1 0 1 1-1 3-1 4-2 1-1 2-2 2-3-0-1-1-2-2-2z'/%3E%3C/defs%3E%3Cpath d='M727 194h21m-21 13h21m6-13h21m-21 13h21m5-13h21m-21 13h21m5-13h21m-21 13h21m6-13h21m-21 13h21m5-13h21m6 0h21m5 0h21m429-78h21m6 0h21m5 0h21m6 0h21m5 0h21m402-78h21m6 0h21m5 0h21m6-13h21m-21 13h21m5-13h21m-21 13h21m6-13h21m-21 13h21m5-13h21m-21 13h21m6-26h21m-21 13h21m-21 13h21m5-26h21m-21 13h21m-21 13h21m5-26h21m-21 13h21m-21 13h21m6-39h21m-21 13h21m-21 13h21m-21 13h21m5-39h21m-21 13h21m-21 13h21m-21 13h21m6-39h21m-21 13h21m-21 13h21m-21 13h21m5-39h21m-21 13h21m-21 13h21m-21 13h21m6-39h21m-21 13h21m-21 13h21m-21 13h21m5-39h21m-21 13h21m-21 13h21m-21 13h21' fill='none' stroke='%23000'/%3E%3Cuse xlink:href='%23f' x='744' y='211'/%3E%3Cuse xlink:href='%23n' x='771' y='211'/%3E%3Cuse xlink:href='%23n' x='797' y='205'/%3E%3Cuse xlink:href='%23f' x='824' y='198'/%3E%3Cuse xlink:href='%23n' x='850' y='198'/%3E%3Cuse xlink:href='%23f' x='877' y='192'/%3E%3Cuse xlink:href='%23n' x='903' y='192'/%3E%3Cuse xlink:href='%23n' x='930' y='185'/%3E%3Cuse xlink:href='%23f' x='956' y='179'/%3E%3Cuse xlink:href='%23n' x='983' y='179'/%3E%3Cuse xlink:href='%23f' x='1009' y='172'/%3E%3Cuse xlink:href='%23n' x='1035' y='172'/%3E%3Cuse xlink:href='%23f' x='1062' y='166'/%3E%3Cuse xlink:href='%23n' x='1088' y='166'/%3E%3Cuse xlink:href='%23n' x='1115' y='159'/%3E%3Cuse xlink:href='%23f' x='1141' y='153'/%3E%3Cuse xlink:href='%23n' x='1168' y='153'/%3E%3Cuse xlink:href='%23f' x='1194' y='146'/%3E%3Cuse xlink:href='%23n' x='1221' y='146'/%3E%3Cuse xlink:href='%23n' x='1247' y='140'/%3E%3Cuse xlink:href='%23f' x='1274' y='133'/%3E%3Cuse xlink:href='%23n' x='1300' y='133'/%3E%3Cuse xlink:href='%23f' x='1327' y='127'/%3E%3Cuse xlink:href='%23n' x='1353' y='127'/%3E%3Cuse xlink:href='%23f' x='1379' y='120'/%3E%3Cuse xlink:href='%23n' x='1406' y='120'/%3E%3Cuse xlink:href='%23n' x='1432' y='114'/%3E%3Cuse xlink:href='%23f' x='1459' y='107'/%3E%3Cuse xlink:href='%23n' x='1485' y='107'/%3E%3Cuse xlink:href='%23f' x='1512' y='101'/%3E%3Cuse xlink:href='%23n' x='1538' y='101'/%3E%3Cuse xlink:href='%23n' x='1565' y='95'/%3E%3Cuse xlink:href='%23f' x='1591' y='88'/%3E%3Cuse xlink:href='%23n' x='1618' y='88'/%3E%3Cuse xlink:href='%23f' x='1644' y='82'/%3E%3Cuse xlink:href='%23n' x='1670' y='82'/%3E%3Cuse xlink:href='%23f' x='1697' y='75'/%3E%3Cuse xlink:href='%23n' x='1723' y='75'/%3E%3Cuse xlink:href='%23n' x='1750' y='69'/%3E%3Cuse xlink:href='%23f' x='1776' y='62'/%3E%3Cuse xlink:href='%23n' x='1803' y='62'/%3E%3Cuse xlink:href='%23f' x='1829' y='56'/%3E%3Cuse xlink:href='%23n' x='1856' y='56'/%3E%3Cuse xlink:href='%23n' x='1882' y='49'/%3E%3Cuse xlink:href='%23f' x='1909' y='43'/%3E%3Cuse xlink:href='%23n' x='1935' y='43'/%3E%3Cuse xlink:href='%23f' x='1962' y='36'/%3E%3Cuse xlink:href='%23n' x='1988' y='36'/%3E%3Cuse xlink:href='%23f' x='2014' y='30'/%3E%3Cuse xlink:href='%23n' x='2041' y='30'/%3E%3Cuse xlink:href='%23n' x='2067' y='23'/%3E%3Cuse xlink:href='%23f' x='2094' y='17'/%3E%3Cuse xlink:href='%23n' x='2120' y='17'/%3E%3Cuse xlink:href='%23f' x='2147' y='10'/%3E%3Cuse xlink:href='%23n' x='2173' y='10'/%3E%3Cuse xlink:href='%23n' x='2200' y='4'/%3E%3Cuse xlink:href='%23f' x='2226' y='-3'/%3E%3Cuse xlink:href='%23n' x='2253' y='-3'/%3E%3Cuse xlink:href='%23f' x='2279' y='-9'/%3E%3Cuse xlink:href='%23n' x='2305' y='-9'/%3E%3C/svg%3E");
background-size: cover;
background-position: 0px 0px;
}

Any assistance would be greatly appreciated!
Thanks!
Matt

Beka Westberg

unread,
Jul 11, 2023, 2:07:04 PM7/11/23
to blo...@googlegroups.com
Hello Matt =)

Could you give some information about what's broken when you try to upgrade to the latest version of Blockly?

Once we know a bit about what's going wrong, it should be much easier to figure out how to fix it! So hopefully with a bit more information we can get this sorted =)

Best wishes,
--Beka

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/blockly/d20c4e81-39fd-4605-861b-27f7f61afe49n%40googlegroups.com.

Maribeth Bottorff

unread,
Jul 11, 2023, 3:37:28 PM7/11/23
to Blockly
Hi Matt,

I noticed a couple problems:
- You are declaring a class called `FieldPitch` but then you end the class declaration and after that start putting methods on the prototype of an object called `Blockly.FieldPitch`. Since Blockly does not have a built-in field there, that object does not exist. All of those methods that start with `Blockly.FieldPitch.prototype` should just be methods in your `FieldPitch` class.
- In the body of the methods, make sure you are just calling other methods/properties in the class, e.g. if `TONES` is a property on the class, you'd want to use it directly instead of `Blockly.FieldPitch.TONES`.
- You need to update the style of calling the superclass. Your code is using the old Closure-style inheritance methods, but in modern times we have stopped using that in the library and just use ES6 classes and inheritance. You started doing that when you declared your class `FieldPitch extends Blockly.FieldTextInput`, but you need to use it consistently by using `super()` to call the superclass's method instead of the closure-style `superClass_.methodName_.call`.

 If you have additional problems revealed after you fix those, then as Beka noted, please do include more information about what's broken including error messages/stack traces, so we can best assist you. Thanks!

Maribeth

Matthew Matz

unread,
Jul 12, 2023, 4:31:13 PM7/12/23
to Blockly
I think I now have it working, thank you so much for the help!

Matthew Matz

unread,
Jul 17, 2023, 7:28:52 PM7/17/23
to Blockly
Good afternoon,

I'm making good progress but I have run into another issue - in v10 - the color field appears to work by changing the color of the whole block, which is problematic when I have a block that has two color fields (foreground and background).  Is there a way to revert the color field back to a clickable area that changes color when you select it instead of the whole block changing?

Maribeth Bottorff

unread,
Jul 17, 2023, 7:36:43 PM7/17/23
to Blockly
Hello,

Could you provide more information about what you're experiencing? A screenshot and your block definition would be most helpful.

I'm not able to replicate what you've described in core. I added two colour fields to the default colour picker block and it behaves how I would expect (see screenshot).

I'll also note that we've made the colour field as a plugin available here, and we plan to remove it from core in v11. So you can get a head start by migrating to the plugin version if you're already working on this bit of your codebase.

Maribeth

Screenshot 2023-07-17 at 4.33.59 PM.png

Maribeth Bottorff

unread,
Jul 17, 2023, 7:38:47 PM7/17/23
to Blockly
I spoke just a moment too soon, I actually can replicate this in the zelos renderer, which is what it appears you are using. Will be back with more info shortly.

Maribeth Bottorff

unread,
Jul 17, 2023, 8:48:03 PM7/17/23
to Blockly
Ok, as far as I know, the full-block colour field has never worked right in zelos if there were multiple colour fields on one block, from the start of us adding this feature in 2019: https://github.com/google/blockly/pull/3461/files

I will file an issue about this tomorrow after a bit more investigation.

Maribeth

Matthew Matz

unread,
Jul 17, 2023, 9:04:09 PM7/17/23
to blo...@googlegroups.com
Okay, thank you.  The priority is low because I think it would be better for me to use separate color blocks in value inputs rather than trying to put two fields into a single block.

I do have one other issue that seems to be a rendering bug.
I have a custom field where the user can create an 8x8 pixel art grid.  When the block is inside of another block's value input, it doesn't render correctly (top blocks) until the field is clicked on, then the block corrects and fixes itself:
Screen Shot 2023-07-17 at 5.58.24 PM.png

The source for the project is at https://github.com/buildwithpiper/piper-make/

Thank you!
Matt

You received this message because you are subscribed to a topic in the Google Groups "Blockly" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/blockly/5Irs315-e54/unsubscribe.
To unsubscribe from this group and all its topics, send an email to blockly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/blockly/312a20ea-5bce-4dfa-979b-6e6a2c9c00c1n%40googlegroups.com.

Christopher Allen

unread,
Jul 18, 2023, 4:14:31 PM7/18/23
to blo...@googlegroups.com
Hi Matthew,

I do have one other issue that seems to be a rendering bug.
I have a custom field where the user can create an 8x8 pixel art grid.  When the block is inside of another block's value input, it doesn't render correctly (top blocks) until the field is clicked on, then the block corrects and fixes itself

I had a quick look at this and unfortunately it's not obvious to me what the problem is.

I did notice, however, that it is only when the field's value is actually changed that the rendering sorts itself out.  I think it might be useful to look into exactly what code is getting run by the field's validator methods and the block's onchange method.  Try commenting out one line at a time from each of these functions until you find the one that, when commented out, prevents the rendering from fixing itself; if it's a function call repeat with the contents of that function.  At some point it should become obvious what "fixes" the rendering, and that will be a big clue about why it isn't working in the first place.

If you discover that it's actually a bug in Blockly do file a bug report so we can fix it.


Best of luck,

Christopher

Maribeth Bottorff

unread,
Jul 18, 2023, 6:11:04 PM7/18/23
to Blockly
I've filed an issue for the colour-field bug here. You are right that it is probably better to have a block with two separate value inputs in which a user can drop colour blocks instead of using two colour fields. That would allow the user to choose to use the "random colour" or "create colour from rgb" or any other blocks that define a colour instead of forcing them to use the simple colour selector box. However if you decide this behavior is important to you we would accept a PR fixing the issue if you choose to work on it :)

As for the rendering bug in your other field, I don't see any problems on the published site you linked, so I'm not much help. I would make sure that the `size_` property of the field is being updated correctly when the field is created. You also might be interested in the field-bitmap plugin we publish, either to use directly, or as inspiration for yours. I noticed on yours that I could not figure out how to unpaint a pixel once I painted it. :)

Maribeth

Mark Friedman

unread,
Jul 18, 2023, 7:05:22 PM7/18/23
to blo...@googlegroups.com
Maribeth,

  To see the issue, click the "Actions" category in the toolbox and select the "set lightshow pixels to" block (which contains the field in question).  Note the difference between how that renders and how the "pixel grid" block renders when you select it from the "Values" category in the toolbox.

-Mark


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.

Matthew Matz

unread,
Jul 18, 2023, 7:41:16 PM7/18/23
to Blockly
I think I figured it out from the advice you all have provided, so thank you!

The `size_` property of the field was using `this.movableGroup_.getBBox()` to determine how big it needed to be - the problem with that is that something wasn't rendered, so this was evaluating to {width: 0, height: 0}.  By hard-coding the size in, it solves the issue.

I am a bit concerned that it's not super intuitive to use, however - the previous version used a mutator that showed a color field when it was "active" that allowed the user to choose a paintbrush color to paint the pixels with.  Since the color field for the zelos renderer is essentially the whole block, that didn't work correctly so I just set it to show a color field when you click the block somewhere other than the pixel grid, allowing you to set the "paintbrush color" and edit the pixels with the new color.

I do like the idea of additional buttons below the grid like the field-bitmap example - maybe there is a way to integrade a color picker into the grid field like the buttons it shows... ??

Christopher Allen

unread,
Jul 20, 2023, 9:38:23 AM7/20/23
to blo...@googlegroups.com
Hi Matthew,
 
The `size_` property of the field was using `this.movableGroup_.getBBox()` to determine how big it needed to be - the problem with that is that something wasn't rendered, so this was evaluating to {width: 0, height: 0}.  By hard-coding the size in, it solves the issue.

I'm glad you found a solution that worked!

Alas I'm not nearly as experienced at frontend (e.g. browser) development as most of the Blockly team, so I was completely mystified.  A colleague and I looked at your code today and we weren't able to understand why the group seems to be rendered before the .getBBox call in some situations but not in others.  We did agree that hard-coding it is a reasonable approach, though, because you know in advance exactly how big it's going to be.

Still, we're curious about what's going on here and have asked another colleague more familiar with rendering if they have any insights; I'll let you know if I learn anything useful.


Christopher




Reply all
Reply to author
Forward
0 new messages