Initialize a Blockly Mutator within JavaScript

3.2k views Asked by At

Hi,

as far as I know, custom blocks in Blockly can be defined wether in JSON or in JavaScript, but how can a mutator be initialized in JavaScript?

with JSON:

Blockly.defineBlocksWithJSONArray([
{....
"mutator": "myMutatorName"
});

Then the Mutator_MIXIN must be defined and with Blockly.Extension.registerMutator('myMutatorName', Blockly.myMutator_MIXIN, null, null) the mutator is added to the Block.

with JavaScript:

Blockly.Blocks['blockName'] = {
 init: function() = {
   ....
   ??? this.setMutator(???)???
   };
}

So how can this be done in JavaScript?

Kind regards

a new one

2

There are 2 answers

1
Luv On

You have to declare how the xml is loaded to dom, and how it is saved to xml and redrawn. Also notice how it attaches a mutator to a block element in case that is the only part you need to reference a mutator already present.

        init: initFunction (Like you have declared.)
        mutationToDom: MutationToDom,
        domToMutation: DomToMutation,
        updateShape_: UpdateShape`

If all you require is to create a reference to a mutator then what you need is an element of this kind, which we will programatically create in a bit:

<mutation mutator_name="true"></mutation>

The following snippet is an example of the extra functions mutationToDom, DomtoMutation UpdateShape which attaches extra input conditionally. I have a block with a checkbox that when enabled, adds an extra input.

   function MutationToDom() {
    var container = document.createElement('mutation');
    var continueOnError = (this.getFieldValue('HasCONTINUE') == 'TRUE');
    container.setAttribute('continueOnError', continueOnError);
    return container;
}

function DomToMutation(xmlElement) {
    var continueOnError = (xmlElement.getAttribute('continueOnError') == 'true');
    this.updateShape_(continueOnError);
}

function UpdateShape(continueOnError) {
    // Add or remove a Value Input.
    if (continueOnError) {
        this.appendValueInput("CONTINUE_ON_ERROR")
            .setCheck('CONTINUE_ON_ERROR');
    } else {
        if (this.childBlocks_.length > 0) {
            for (var i = 0; i < this.childBlocks_.length; i++) {
                if (this.childBlocks_[i].type == 'continue_on_error') {
                    this.childBlocks_[i].unplug();
                    break;
                }
            }
        }
        this.removeInput('CONTINUE_ON_ERROR');
    }
}
0
Vadimz On

I might be just a little bit late here, but I'll leave the answer anyway for those who need a bit more concrete example.

In JavaScript, you don't actually need to bind a mutator to your block, you just need to define mutationToDom() and domToMutation(xmlElement) functions, like so:

Blockly.Blocks['my_custom_block'] = {
  init() {
    // Define your basic block stuff here
  },
  // Mutator functions
  mutationToDom() {
    let container = document.createElement('mutation');

    // Bind some values to container e.g. container.setAttribute('foo', 3.14);

    return container;
  },
  domToMutation(xmlElement) {
    // Retrieve all attributes from 'xmlElement' and reshape your block
    // e.g. let foo = xmlElement.getAttribute('foo');
    // this.reshape(foo);
  },
  // Aux functions
  reshape(param){
    // Reshape your block...
  }
}

Blockly will automagically take care of the rest and allow you to treat your block as dynamic one.

And if you need to used Mutator Editor UI, you must define decompose(workspace) and compose(containerBlock) functions and call this.setMutator(...) to set which blocks are used in the Mutator Editor UI, like so:

Blockly.Blocks['my_custom_block'] = {
    init() {
        // Define your basic block stuff here

        // Set all block that will be used in Mutator Editor UI, in this
        // case only 'my_block_A' and 
        this.setMutator(new Blockly.Mutator(['my_block_A', 'my_block_B']));
    },
    // Mutator functions
    mutationToDom() {
        // Same as previous example
    },
    domToMutation(xmlElement) {
        // Same as previous example
    },
    decompose(workspace) {
        // Decomposeyour block here
    },
    compose(containerBlock) {
        // Compose your block here
    },
    // Aux functions
    reshape(param){
        // Same as previous example
    }
}

Hope that these short examples help someone :)