Adding Inputs and Outputs Dynamically to a Block and using the miniature version of the created block.

57 views
Skip to first unread message

Rajtilak Oleti

unread,
Dec 5, 2024, 8:48:23 AMDec 5
to Blockly
Hello Blockly community,

I have been working on  implementing custom blocks and have encountered a couple of challenges for which I would appreciate your guidance.

Context:
1. I have created a workspace (Default workspace) and a button on the workspace called "Block Builder".

Clicking this button switches to a new workspace called "Block Builder", in which I resets the workspace and toolbox, and loads different categories into the toolbox.

In this "Block Builder" workspace, I have defined a block called "Sequencer", which behaves similarly to a "Function" block.
sequencer block in block builder workspace.png|

2. The Sequencer Block definition is as follows:
{
  "type": "sequencer",
  "message0": "%1 sequencer %2 \n why %3 \n what %4 %5",
  "args0": [
    { "type": "field_image", "src": BlockLogo.IMAGE, "width": BlockLogo.WIDTH, "height": BlockLogo.HEIGHT, "alt": BlockLogo.ALT_TEXT },
    { "type": "field_input", "name": "seqFunctionName" },
    { "type": "field_input", "name": "why" },
    { "type": "field_input", "name": "what" },
    { "type": "field_input", "name": "functionId" }
  ],
  "message1": "%1",
  "args1": [
    { "type": "input_statement", "name": "CODE" }
  ],
  "inputsInline": true,
  "colour": BlockColors.VARIABLE_DYNAMIC_BLOCKS,
  "tooltip": "This block does the job of naming a sequence and contributes to canvas navigation",
  "helpUrl": ""
}

it's code is as follows 
sequencer blocks code in block builder workspace.png

3. when click on save button, I save the block data in the following format:
block_details = {
  "functionId": "1234",
  "seqFunctionName": "Get Points",
  "snippet": "function GetPoints() {\n\n\n}\n",
  "xml": "<block ...></block>",
  "what": "test",
  "why": "test",
  "type": "fn_1234"
};

4. Upon switching back to the default workspace, I dynamically create a miniature version of the sequencer block
miniature version of sequencer block in default workspace.png

using the below method:

"dynamicBlockBuilder.sequencerFunctionblockInit(block_details);"

the miniature block is created dynamically using the following method
export class dynamicBlockBuilder {
  // Method to build a block dynamically
  static sequencerFunctionblockInit(block: any) {
    Blockly.Blocks[block.type] = {
      init: function () {
        this.appendDummyInput()
          .appendField(this.getBlockImage(), 'blockImage')
          .appendField(new Blockly.FieldLabel(block.seqFunctionName), 'seqFunctionName')
          .appendField(new Blockly.FieldLabel(block.functionId), 'functionId')
          .appendField(new Blockly.FieldTextInput(block.xml), 'xml')
          .appendField(new Blockly.FieldTextInput(block.snippet), 'snippet')
          .appendField(new Blockly.FieldTextInput(block.why), 'why')
          .appendField(new Blockly.FieldTextInput(block.what), 'what')
        this.setColour(270);
        this.setPreviousStatement(true, null);
        this.setNextStatement(true, null);
        this.setTooltip(`Why: ${block.why}, What: ${block.what}`);
        this.setHelpUrl('');
        this.updateBlockShape();
      },

      // Create an image field with specified source, width, and height
      getBlockImage() {
        const src = BlockLogo.IMAGE;
        const width = BlockLogo.WIDTH;
        const height = BlockLogo.HEIGHT;
        const alt = BlockLogo.ALT_TEXT
        return new Blockly.FieldImage(src, width, height, alt);
      },

      // Hiding the xml, snippet, and why/what fields of the block.
      updateBlockShape: function () {
        this.getField('functionId').setVisible(false);
        this.getField('xml').setVisible(false);
        this.getField('snippet').setVisible(false);
        this.getField('why').setVisible(false);
        this.getField('what').setVisible(false);
      }
    };

    // Code generation logic
    javascriptGenerator.forBlock[block.type] = function (block: any) {
      var functionName = block.getFieldValue('seqFunctionName');
      var code = block.getFieldValue('snippet');
      code = `\n${functionName}()\n`;
      return code;
    };
  };
}


My Challenges:
 In Block Builder workspace
  1. Dynamically Adding Inputs and Outputs to the Sequencer Block:
I want the Sequencer Block in the "Block Builder" workspace to allow adding inputs (e.g., parameters) and specifying an output, similar to the functionality of the "Function" block.
The number of inputs should be adjustable dynamically by the user.

 In default workspace
2. When the sequencer block is saved and a miniature version is created in the default workspace, the block should display the inputs defined earlier.
For example, if the sequencer block in the "Block Builder" workspace had 3 inputs, the miniature block in the default workspace should also show those inputs.

Could you please guide me on:
1. How to implement dynamic inputs and outputs for the Sequencer Block, similar to the functionality in the "Function" block?

function block with dynamic inputs and output.png

2. How to ensure that the miniature version of the sequencer block in the default workspace reflects the correct inputs and outputs dynamically similar to the block in below image?
function call block with dynamic inputs.png
 






feni...@google.com

unread,
Dec 6, 2024, 4:39:10 PMDec 6
to Blockly
Hello,

As you have identified, your desired behaviour is very similar to the behaviour of the procedure blocks. We actually have a set of procedure blocks that can be defined in one workspace and then used in another. 

I think you can achieve much of what you want by looking at or modifying that code. In particular, the code in blocks.ts defines the blocks and their associated mutators and mixins, which are responsible for modifying the shape of the block to match the shared procedure model.

Rachel

Reply all
Reply to author
Forward
0 new messages