Skip to content

Instantly share code, notes, and snippets.

@10MINT
Last active January 27, 2024 18:54
Show Gist options
  • Select an option

  • Save 10MINT/970bff6abf4e0e6360c3a40a001c6ff5 to your computer and use it in GitHub Desktop.

Select an option

Save 10MINT/970bff6abf4e0e6360c3a40a001c6ff5 to your computer and use it in GitHub Desktop.
Blockly mutator with checkboxes which correspond to values in a block
/**
* @author @10MINT (redpandadevs@gmail.com)
*/
'use strict';
goog.provide('Blockly.Constants.Border');
goog.require('Blockly');
goog.require('Blockly.Blocks');
const FIELDS = ["color", "width"];
const TYPES = ["Colour", "Number"];
const COLOR = 60;
const TOOLTIP = "Example border object";
const HELPURL = "www.example.com";
Blockly.Blocks['border'] = {
init: function() {
this.appendDummyInput()
.appendField("Border");
this.setOutput(true, "Border");
this.setColour(COLOR);
this.setTooltip(TOOLTIP);
this.setHelpUrl(HELPURL);
this.jsonInit({"mutator": "border_mutator"});
this.inputs_ = Array(FIELDS.length).fill("FALSE");
}
};
Blockly.Blocks['border_mutator'] = {
init: function() {
for (let i = 0; i < FIELDS.length; i++) {
this.appendDummyInput()
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(FIELDS[i])
.appendField(new Blockly.FieldCheckbox(false), FIELDS[i]);
}
this.setColour(COLOR);
this.setTooltip("");
this.setHelpUrl("");
}
};
Blockly.Constants.Border.BORDER_MUTATOR_MIXIN = {
connections_: Array(FIELDS.length).fill(null),
/**
* Create XML to represent the number inputs.
* @return {Element} XML storage element.
* @this Blockly.Block
*/
mutationToDom: function() {
var container = document.createElement('mutation');
for (let i = 0; i < this.inputs_.length; i++) {
if (this.inputs_[i] == "TRUE") {
container.setAttribute(FIELDS[i], this.inputs_[i]);
}
}
return container;
},
/**
* Parse XML to restore the inputs.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
domToMutation: function(xmlElement) {
for (let i = 0; i < this.inputs_.length; i++) {
this.inputs_[i] = xmlElement.getAttribute(FIELDS[i].toLowerCase());
}
this.updateShape_();
},
/**
* Populate the mutator's dialog with this block's components.
* @param {!Blockly.Workspace} workspace Mutator's workspace.
* @return {!Blockly.Block} Root block in mutator.
* @this Blockly.Block
*/
decompose: function(workspace) {
var containerBlock = workspace.newBlock('border_mutator');
for (let i = 0; i < this.inputs_.length; i++) {
containerBlock.setFieldValue(this.inputs_[i], FIELDS[i]);
}
containerBlock.initSvg();
return containerBlock;
},
/**
* Reconfigure this block based on the mutator dialog's components.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this Blockly.Block
*/
compose: function(containerBlock) {
// Get check box values
for (let i = 0; i < this.inputs_.length; i++) {
this.inputs_[i] = containerBlock.getFieldValue(FIELDS[i]);
}
this.updateShape_();
// Reconnect any child blocks
for (let i = 0; i < FIELDS.length; i++) {
if (this.getInput(FIELDS[i])) {
Blockly.Mutator.reconnect(this.connections_[i], this, FIELDS[i]);
}
}
},
/**
* Store pointers to any connected child blocks.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this Blockly.Block
*/
saveConnections: function(containerBlock) {
for (let i = 0; i < this.inputs_.length; i++) {
var input = this.getInput(FIELDS[i]);
if (input) {
this.connections_[i] = input && input.connection.targetConnection;
}
}
},
/**
* Modify this block to have the correct number of inputs.
* @this Blockly.Block
* @private
*/
updateShape_: function() {
// Delete everything.
FIELDS.forEach(element => {
if (this.getInput(element)) {
this.removeInput(element);
}
});
// Rebuild block.
for (let i = 0; i < this.inputs_.length; i++) {
if (this.inputs_[i] == "TRUE") {
this.appendValueInput(FIELDS[i])
.setCheck(TYPES[i])
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(FIELDS[i]);
}
}
}
};
Blockly.Extensions.registerMutator('border_mutator', Blockly.Constants.Border.BORDER_MUTATOR_MIXIN, null, [""]);
@pierrickgrand
Copy link
Copy Markdown

pierrickgrand commented Jan 27, 2024

Hello @10MINT ,
I tried to use your code, making some modifications to don't use goog.require :
` const FIELDS = ["color", "width"];
const TYPES = ["Colour", "Number"];
const COLOR = 60;
const TOOLTIP = "Example border object";
const HELPURL = "www.example.com";

Blockly.Blocks["border"] = {
  init: function () {
    this.appendDummyInput().appendField("Border");
    this.setOutput(true, "Border");
    this.setColour(COLOR);
    this.setTooltip(TOOLTIP);
    this.setHelpUrl(HELPURL);
    this.jsonInit({ mutator: "border_mutator" });
    this.inputs_ = Array(FIELDS.length).fill("FALSE");
  },
};

Blockly.Blocks["border_mutator"] = {
  init: function () {
    for (let i = 0; i < FIELDS.length; i++) {
      this.appendDummyInput()
        //.setAlign(Blockly.ALIGN_RIGHT)
        .appendField(FIELDS[i])
        .appendField(new Blockly.FieldCheckbox("FALSE"), FIELDS[i]);
    }
    this.setColour(COLOR);
    this.setTooltip("");
    this.setHelpUrl("");
  },
};
Blockly.Constants = {};
Blockly.Constants.Border = {};
Blockly.Constants.Border.BORDER_MUTATOR_MIXIN = {
  connections_: Array(FIELDS.length).fill(null),

  /**
   * Create XML to represent the number inputs.
   * @return {Element} XML storage element.
   * @this Blockly.Block
   */
  mutationToDom: function () {
    var container = document.createElement("mutation");
    for (let i = 0; i < this.inputs_.length; i++) {
      if (this.inputs_[i] == "TRUE") {
        container.setAttribute(FIELDS[i], this.inputs_[i]);
      }
    }
    return container;
  },
  /**
   * Parse XML to restore the inputs.
   * @param {!Element} xmlElement XML storage element.
   * @this Blockly.Block
   */
  domToMutation: function (xmlElement) {
    for (let i = 0; i < this.inputs_.length; i++) {
      this.inputs_[i] = xmlElement.getAttribute(FIELDS[i].toLowerCase());
    }
    this.updateShape_();
  },
  /**
   * Populate the mutator's dialog with this block's components.
   * @param {!Blockly.Workspace} workspace Mutator's workspace.
   * @return {!Blockly.Block} Root block in mutator.
   * @this Blockly.Block
   */
  decompose: function (workspace) {
    var containerBlock = workspace.newBlock("border_mutator");
    for (let i = 0; i < this.inputs_.length; i++) {
      containerBlock.setFieldValue(this.inputs_[i], FIELDS[i]);
    }
    containerBlock.initSvg();
    return containerBlock;
  },
  /**
   * Reconfigure this block based on the mutator dialog's components.
   * @param {!Blockly.Block} containerBlock Root block in mutator.
   * @this Blockly.Block
   */
  compose: function (containerBlock) {
    // Get check box values
    for (let i = 0; i < this.inputs_.length; i++) {
      this.inputs_[i] = containerBlock.getFieldValue(FIELDS[i]);
    }
    this.updateShape_();
    // Reconnect any child blocks
    for (let i = 0; i < FIELDS.length; i++) {
      if (this.getInput(FIELDS[i])) {
        Blockly.Mutator.reconnect(this.connections_[i], this, FIELDS[i]);
      }
    }
  },
  /**
   * Store pointers to any connected child blocks.
   * @param {!Blockly.Block} containerBlock Root block in mutator.
   * @this Blockly.Block
   */
  saveConnections: function (containerBlock) {
    for (let i = 0; i < this.inputs_.length; i++) {
      var input = this.getInput(FIELDS[i]);
      if (input) {
        this.connections_[i] = input && input.connection.targetConnection;
      }
    }
  },
  /**
   * Modify this block to have the correct number of inputs.
   * @this Blockly.Block
   * @private
   */
  updateShape_: function () {
    // Delete everything.
    FIELDS.forEach((element) => {
      if (this.getInput(element)) {
        this.removeInput(element);
      }
    });
    // Rebuild block.
    for (let i = 0; i < this.inputs_.length; i++) {
      if (this.inputs_[i] == "TRUE") {
        this.appendValueInput(FIELDS[i])
          .setCheck(TYPES[i])
          .setAlign(Blockly.ALIGN_RIGHT)
          .appendField(FIELDS[i]);
      }
    }
  },
};

Blockly.Extensions.registerMutator(
  "border_mutator",
  Blockly.Constants.Border.BORDER_MUTATOR_MIXIN,
  null,
  [""]
);`

but when i click on the mutation gear button on the block an error happen: blockly_compressed.js:6 Uncaught Error: Expected to find a 'type' property, defining the block type
at appendPrivate$$module$build$src$core$serialization$blocks (blockly_compressed.js:6:81707)
did you have an idea of what can cause this problem
thanks in advance for your answer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment