Custom collapsed version of Block

183 views
Skip to first unread message

Alejandro Ferrante

unread,
Nov 12, 2021, 3:00:15 PM11/12/21
to Blockly
Hi, what i am trying to achieve is given a complex block structure (made of diffetent nested blocks) , have a simplified collapsed version that hides it's internal representation and working.
 samp.png
This way people using it don't need to know how it is built and it's use may be much simplier and clear.

I've noticed words like "custom renderer" and "mutator" being thrown around but even having brwsed several posts in this group i am not sure where to start.

Does anyone have an idea of how to achieve this? Or at least what topics/examples i should look into?
Thanks in advance.

Maribeth Bottorff

unread,
Nov 12, 2021, 8:38:35 PM11/12/21
to Blockly
Hello,

I would say that conceptually, this is what functions are for. A function encapsulates some bit of logic that can be used somewhere else without the caller having to understand the internal workings of the function. So I would suggest that you use functions and try to point your users to using functions to encapsulate bits of logic. Then you have the benefits of built-in functionality, ability to use names to reference them, and you're using real programming concepts that would apply to other languages. 

You can also already collapse blocks. When you collapse a block, all of its children will also be hidden, but not any of its sibling blocks (next/previous connections). As a much simpler version of this, you could add a context menu item to collapse an entire stack of blocks. To do that, I would reference the implementation of the default "collapse" option (here is the "collapse all" option and the collapse single block is in this same file) and implement a similar one that collapses the block that was clicked on and for the block in its next connection, and so on. You could have a look at how the different renderers handle the look of collapsed blocks (geras uses the jagged edge, zelos uses a question mark, etc)

If you are against those ideas, then yes, you would have to build this out yourself with some significant customizations. A mutator is a way to add additional state to a block, so you could for example add a mutator to all blocks which would toggle the hidden state for the block. And to get that overlay, it would require going pretty in-depth on the rendering code. We have a codelab for making a custom renderer, but it doesn't go further than customizing connection shapes. Beyond that we don't have any written documentation explaining exactly how a renderer works, so you'd have to dive into the rendering code to figure that out (you can post here for help, but I don't know of anyone who has done a complex custom rendering task like this).

Message has been deleted

fu6...@gmail.com

unread,
Nov 14, 2021, 1:56:15 AM11/14/21
to Blockly
Hi, 
Share my sample code. Hope this helps.


Blockly.Blocks["mutation_container"] = {
  init: function() {
    this.appendDummyInput()   
.appendField("Hello World");   
    this.setNextStatement(true, null);
  }
};

Blockly.Blocks["mutation_test"] = {
  init: function() {
    this.appendValueInput("inputValue")
        .setCheck("Number");
    this.setInputsInline(true);
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(100);
    this.updateShape_();    
    this.setMutator(new Blockly.Mutator(""));
  },
  mutationToDom: function (workspace) {
    var container = document.createElement('mutation');
    return container;
  },
  domToMutation: function (xmlElement) {
    this.updateShape_();
  },
  decompose: function (workspace) {
    var containerBlock = workspace.newBlock('mutation_container');
    containerBlock.initSvg();
var myBlock1 = workspace.newBlock("controls_if");
myBlock1.initSvg();
containerBlock.nextConnection.connect(myBlock1.previousConnection);
var myBlock2 = workspace.newBlock("logic_compare");
myBlock2.initSvg();
myBlock2.setFieldValue("GT","OP");
myBlock1.getInput("IF0").connection.connect(myBlock2.outputConnection);
var myBlock3 = workspace.newBlock("math_number");
myBlock3.initSvg();
myBlock2.getInput("B").connection.connect(myBlock3.outputConnection);
if (this.getInputTargetBlock("inputValue")) {
var myBlock4 = workspace.newBlock(this.getInputTargetBlock("inputValue").type);
myBlock4.initSvg();
myBlock2.getInput("A").connection.connect(myBlock4.outputConnection);
if (this.getInputTargetBlock("inputValue").type=="math_number") {
myBlock4.setFieldValue(this.getInputTargetBlock("inputValue").getFieldValue("NUM"),"NUM");
}
}
    return containerBlock;
  },
  compose: function(containerBlock) {   
    this.updateShape_();
  },
  saveConnections: function(containerBlock) {
  },
  updateShape_: function() {
  }
};


mutation_container.mp4

Alejandro Ferrante

unread,
Nov 15, 2021, 8:41:12 AM11/15/21
to Blockly
Wow, this is exactly what i was looking for, thanks a lot!

How would this work from a behavioural point of view? i'm trying to understand the code.
will mutation_container automatically behave as the HelloWorld block compound describes?
I feel like there are some extra steps missing.

fu6...@gmail.com

unread,
Nov 18, 2021, 3:29:09 AM11/18/21
to Blockly
Hi,

Blockly.Arduino['mutation_test'] = function(block) {
  var value_inputValue = Blockly.Arduino.valueToCode(block, 'inputValue', Blockly.Arduino.ORDER_ATOMIC);

  var code = "if (" + value_inputValue + " > 0) {\n" +
"  etc...\n" +
"}\n";
  return code;
};
mutation_test.mp4

Alejandro Ferrante

unread,
Nov 18, 2021, 9:17:50 AM11/18/21
to Blockly
Thanks a lot!
Reply all
Reply to author
Forward
0 new messages