update block input number based on passed parameter

238 views
Skip to first unread message

Sabrina K

unread,
Apr 7, 2020, 5:14:29 AM4/7/20
to Blockly

Hey everyone,

I want to implement structs with Blockly (define-struct) but i have some difficulties with the make-block. 
I want to pass the number (number at least, names would be even better) of associated struct-fields to the make-struct block definition, so that the make-block is dynamically updated with the right amount of input fields (one for every struct-field).
The problem is that the variables.js file in core and my structs.js file in blocks can't really communicate? I tried using the <data> tag and tried something with setAttribute but it didn't work.

Here is my code:

variables.js:

/**
 * Construct the blocks required by the flyout for the structs category.
 * @param {!Blockly.Workspace} workspace The workspace this flyout is for.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.Variables.flyoutStructBlocks = function(workspace) {
  var structs = workspace.getVariablesOfType('Struct');
  var struct_fields = workspace.getVariablesOfType('Struct-Field');
  var xmlList = [];
       
  if (structs.length > 0) {
    var mostRecentStruct = structs[structs.length - 1];
      
    if (Blockly.Blocks['structs_is']) {
      var block = Blockly.utils.xml.createElement('block');
      block.setAttribute('type', 'structs_is');
      block.setAttribute('gap', 24);
      block.appendChild(Blockly.Variables.generateVariableFieldDom(
          mostRecentStruct));
      xmlList.push(block);
    }
      
    if (Blockly.Blocks['structs_make']) {
        
      for (var i = 0; i < structs.length; i++) {
     
        var block = Blockly.utils.xml.createElement('block');
        var fields_arr = Blockly.Variables.getStructFields(workspace,toString(structs[i].name));
          
        var gap = 24;
        var blockText = '<xml>' +
              '<block type="structs_make" gap="' + gap + '">' +
              '<data>' + fields_arr.length + '</data>' +
              '<field name="VAR" variabletype="Struct">' +
              structs[i].name +
              '</field>' +
              '</block>' +
              '</xml>';
        var block = Blockly.Xml.textToDom(blockText).firstChild;
        
        xmlList.push(block);
           
      }
    }
      
  }
    
  if (struct_fields.length > 0) {
      
    if (Blockly.Blocks['structs_field_get']) {
      // struct_fields.sort(Blockly.VariableModel.compareByName);
      
      var block = Blockly.utils.xml.createElement('block');
      block.setAttribute('type', 'structs_field_get');
      block.setAttribute('gap', 24);
      block.appendChild(Blockly.Variables.generateVariableFieldDom(
          struct_fields[struct_fields.length - 1]));
      xmlList.push(block);
    }
  }
    
  return xmlList;
};


structs.js:

Blockly.Blocks['structs_make'] = {
      
    
  init: function() {
      
    this.appendDummyInput()
        .appendField("(make-")
        .appendField(new Blockly.FieldVariable("", null, ['Struct'], 'Struct'), "VAR");
    
    for (var i = 0; i < parseInt(this.data); i++) {
      this.appendValueInput("FIELD" + i).setCheck(null);
    }
      
    this.appendDummyInput().appendField(")");
    
    this.setInputsInline(true);
    this.setOutput(true, "Struct");
    this.setStyle("struct_blocks");
    this.setTooltip(Blockly.Msg['STRUCTS_MAKE_TOOLTIP']);
    this.setEditable(false);
  },
    
};


thanks for your help!

Beka Westberg

unread,
Apr 7, 2020, 12:43:01 PM4/7/20
to Blockly
Hello,

I think you're on the right track! The problem is that the .data property isn't set until after your init function is called. This doesn't have to do with your custom category, that looks great! This is just how data tags work.

I think what you should look at doing instead is using a mutator. My understanding is that data tags are meant for things like code generation. While mutators are used for changing the shape of the block (and also code generation). So a mutator should work better with what you want to do.

I hope that helps! If you have any further questions please definitely reply!
--Beka

Sabrina K

unread,
Apr 8, 2020, 3:48:25 AM4/8/20
to Blockly

Hey!

thanks for your tipp, I already used mutators for some other blocks.
But I don't want to use them with the make-block because it should be a function with fixed inputs for each created struct.

I thought about changing some other parts of the code, maybe you could say me if these changes would be possible?

- can I pass a parameter with the init function like this? and if yes, how do I pass that parameter in my variables.js file?
init: function(num) {...}

- can I access the name of the variable (
block.appendChild(Blockly.Variables.generateVariableFieldDom(
            structs[i]));
) in structs.js? I tried with this.getField("VAR").getText() but I think it's not set when the block is created?


right now I changed to fixed blocks with n inputs (0-5) but dynamic would be much nicer.

my updated variables.js:
if (Blockly.Blocks['structs_make_0']) {
        
      for (var i = 0; i < structs.length; i++) {
     
        var block = Blockly.utils.xml.createElement('block');
        var fields_num = Blockly.Variables.STRUCTS[structs[i].name].length;
          
        block.setAttribute('type', 'structs_make_' + fields_num);
        block.setAttribute('gap', 24);
        block.appendChild(Blockly.Variables.generateVariableFieldDom(
            structs[i]));
        
        xmlList.push(block);
           
      }
    }


Thanks for your help!

Beka Westberg

unread,
Apr 8, 2020, 3:40:33 PM4/8/20
to Blockly
Hello,

Firstly, let me say I think you still may want to use mutators. Mutators don't mean the user needs to be able to to edit the block. You can have a completely hidden mutator that the user doesn't interact with. A mutator just deals with per-block data that round-trips through XML and can be used to edit the shape of the block. If you want to create a mutator that the user doesn't interact with, just define the mutationToDom and domToMutation functions without defining compose and decompose.

Secondly let me adress some of your other ideas :D

> can I pass a parameter with the init function like this? and if yes, how do I pass that parameter in my variables.js file?

Sadly you cannot :/ init() is called by blockly core internally and there's no way for you to modify how it gets called.

>  can I access the name of the variable in structs.js?

So your `block.appendChild(Blockly.Variables.generateVariableFieldDom(structs[i]));` is building the XML representation of the block. Your init function() gets called which creates the block. /Then/ all of that XML that you built gets deserialized and the values get assigned to the block you created in init. So in short, no :/ you can't access any of that XML data from your init method b/c it hasn't been applied yet.

That's the great thing about mutations, is they allow you to access data from the XML to modify your block.

Sorry that that wasn't super encouraging :/ If there's anything else I can do to help please tell me! Someone else may also have ideas for you.
--Beka

Sabrina K

unread,
Apr 9, 2020, 3:14:56 AM4/9/20
to Blockly
thank you so much for your help!!
i will try using the mutators :)

Sabrina K

unread,
Apr 9, 2020, 11:17:58 AM4/9/20
to Blockly



the mutation worked! but now I have a new problem.

when I drag the make-block in the workspace, the next time I open the struct category there is a new unwanted struct. That happens for every time I open the category again.

Here's a picture and my code (the i is unwanted):

unwanted_make.png


Blockly.Variables.flyoutStructBlocks = function(workspace) {
  var structs = workspace.getVariablesOfType('Struct');
  console.log(structs);
    
  var struct_fields = workspace.getVariablesOfType('Struct-Field');
  var xmlList = [];
       
  if (structs.length > 0) {
    // New variables are added to the end of the variableModelList.
    var mostRecentStruct = structs[structs.length - 1];
      
    if (Blockly.Blocks['structs_is']) {
      var block = Blockly.utils.xml.createElement('block');
      block.setAttribute('type', 'structs_is');
      block.setAttribute('gap', 24);
      block.appendChild(Blockly.Variables.generateVariableFieldDom(
          mostRecentStruct));
      xmlList.push(block);
    }
      
    if (Blockly.Blocks['structs_make']) {
        
      for (var i = 0; i < structs.length; i++) {
     
        var block = Blockly.utils.xml.createElement('block');
        var fields_num = Blockly.Variables.getFieldNum(workspace, structs[i].name);
        var gap = 24;
          
        var blockText = '<xml>' +
                          '<block type="structs_make" gap="' + gap + '">' +
                          '<field name="VAR" variabletype="Struct">' + structs[i].name + '</field>' +
                          '<mutation items="' + fields_num + '"></mutation> ' +
                          '</block>' +
                          '</xml>';
          
        var block = Blockly.Xml.textToDom(blockText).firstChild;
        xmlList.push(block);
      }
    }
  }
    
  if (struct_fields.length > 0) {
      
    if (Blockly.Blocks['structs_field_get']) {
      // struct_fields.sort(Blockly.VariableModel.compareByName);
      
      var block = Blockly.utils.xml.createElement('block');
      block.setAttribute('type', 'structs_field_get');
      block.setAttribute('gap', 24);
      block.appendChild(Blockly.Variables.generateVariableFieldDom(
          struct_fields[struct_fields.length - 1]));
      xmlList.push(block);
    }
  }
    
  return xmlList;
};






// block:

Blockly.Blocks['structs_make'] = {
  init: function() {
      
    this.appendDummyInput()
        .appendField("(make-")
        .appendField(new Blockly.FieldVariable("", null, ['Struct'], 'Struct'), "VAR");
      
    this.updateShape_();
    
    this.setInputsInline(true);
    this.setOutput(true, "Struct");
    this.setStyle("struct_blocks");
    this.setTooltip(Blockly.Msg['STRUCTS_MAKE_TOOLTIP']);
    this.setEditable(false);
  },
    
    
  /**
   * Create XML to represent list inputs.
   * @return {!Element} XML storage element.
   * @this {Blockly.Block}
   */
  mutationToDom: function() {
    var container = Blockly.utils.xml.createElement('mutation');
    container.setAttribute('items', this.itemCount_);
    return container;
  },
  /**
   * Parse XML to restore the list inputs.
   * @param {!Element} xmlElement XML storage element.
   * @this {Blockly.Block}
   */
  domToMutation: function(xmlElement) {
    this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
    this.updateShape_();
  },
    
  /**
   * Modify this block to have the correct number of inputs.
   * @private
   * @this {Blockly.Block}
   */
  updateShape_: function() {
    this.removeInput('END', true);
      
    // Add inputs.
    for (var i = 0; i < this.itemCount_; i++) {
        
      if (!this.getInput('FIELD' + i)) {
        this.appendValueInput('FIELD' + i);
      }
    }
    
    this.appendDummyInput('END').appendField(')');
  }
};



could it be that the call  workspace.getVariablesOfType('Struct'); counts the struct-variable one more time after every workspace drag?

Thanks for your help!


Coda Highland

unread,
Apr 9, 2020, 11:39:56 AM4/9/20
to blo...@googlegroups.com
Blockly creates a new block to put it in the toolbox. This of course is going to trigger your event handlers.

Fortunately, you can tell when this happens: block.isInFlyout will be true. You can refrain from making the variable until the block gets dropped into the workspace.

/s/ Adam

--
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/85c0f74d-5a2d-4b89-aa8b-787c37a8d82a%40googlegroups.com.

Sabrina K

unread,
Apr 9, 2020, 1:40:32 PM4/9/20
to Blockly
Can you tell me where and how to use the isInFlyout in my code? Couldn't find good examples in the documentation.

Thank you!
To unsubscribe from this group and stop receiving emails from it, send an email to blo...@googlegroups.com.

Coda Highland

unread,
Apr 9, 2020, 4:17:28 PM4/9/20
to blo...@googlegroups.com
Ah, sorry, I actually don't know where those variables are getting created in your application. I might have been wrong about that advice, because now that I'm looking at it more closely I'm not exactly sure of the root cause.

(Yay for Racket though, I've been a big fan since I learned it in 2003, though the language has changed a lot since then and I'm more than a little rusty.)

/s/ Adam

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/91ab8b72-546c-4a11-853c-a8061641bcc6%40googlegroups.com.

Sabrina K

unread,
Apr 10, 2020, 4:19:18 AM4/10/20
to Blockly

The struct variable is getting created in a slightly changed version of the createVariableButtonHandler function:

Blockly.Variables.createStructButtonHandler = function(
    workspace, opt_callback, opt_type) {
  var type = opt_type || 'Struct';
    
  // This function needs to be named so it can be called recursively.
  var promptAndCheckWithAlert = function(defaultName) {
    Blockly.Variables.promptStructName(Blockly.Msg['NEW_STRUCT_TITLE'], defaultName,
        function(text) {
          if (text) {
              
            // prompt struct-properties: struct-name:field-name1,field-name2,...
            var input = text.replace(/\s+/g, "");
            var struct_list = input.split(":");
            var struct_name = struct_list[0];
            var field_list;
            
            if (struct_list.length > 1) {
              field_list = struct_list[1].split(",");
            }
            
            var existing =
                Blockly.Variables.nameUsedWithAnyType_(struct_name, workspace);
              
            if (existing) {
              if (existing.type == type) {
                var msg = Blockly.Msg['STRUCT_ALREADY_EXISTS'].replace(
                    '%1', existing.name);
              } else {
                var msg =
                    Blockly.Msg['VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE'];
                msg = msg.replace('%1', existing.name).replace('%2', existing.type);
              }
              Blockly.alert(msg,
                  function() {
                    promptAndCheckWithAlert(text);  // Recurse
                  });
           
            } else if (struct_name == 'posn') {
              var msg = Blockly.Msg['STRUCT_ALREADY_EXISTS'].replace(
                  '%1', 'posn');
              Blockly.alert(msg,
                  function() {
                    promptAndCheckWithAlert(text);  // Recurse
                  });
                
            } else {
              // No conflict
              // create struct
                
              // store structs with fields to change all components later
              // (-> rename, delete)
              Blockly.Variables.updateStructs(struct_name, field_list);
                    
              workspace.createVariable(struct_name, type);
                                
              if (struct_list.length > 1) { // struct mit fields
                for (var i = 0; i < field_list.length; i++) {
                    
                  var field_name = struct_name + "-" + field_list[i];
                  workspace.createVariable(field_name, 'Struct-Field');
                }
              }
                
              if (opt_callback) {
                opt_callback(text);
              }
            }
          } else {
            // User canceled prompt.
            if (opt_callback) {
              opt_callback(null);
            }
          }
        });
  };
  promptAndCheckWithAlert('');
};


i also save the input names in a global STRUCTS array which works absolutely fine but I can't find the source of the bug.

Beka Westberg

unread,
Apr 10, 2020, 3:17:39 PM4/10/20
to Blockly
Hello,

With your console.log(structs) call, what is it outputting? I assume you're seeing the erroneous i variable? I just want to confirm it's not categories being derpy with their xml parsing.

To help with debugging you might also want to check out the events system. This sends out an event whenever a var is created or deleted, so it could help you track down the issue.

This is definitely a weird issue! But with enough searching I'm pretty sure we can track it down =)
--Beka

Sabrina K

unread,
Apr 10, 2020, 3:43:40 PM4/10/20
to Blockly

i will check out the Blockly.Events.VAR_CREATE thanks for the tip!

Here's a screenshot of the issue - when I drag the created make-block into the workspace the next time I open the structs category a new struct variable (i, j, k, ...) appears in the VariableModel

Screenshot.png

Sabrina K

unread,
Apr 10, 2020, 3:50:08 PM4/10/20
to Blockly
function onVariableCreate(event) {
        if (event.type == Blockly.Events.VAR_CREATE &&
            event.varType == 'Struct') {
            alert('new struct var')
            workspace.removeChangeListener(onVariableCreate);
        }
    }
    workspace.addChangeListener(onVariableCreate); 

I get a message for every struct variable that I created with the create struct button but no message for the erroneous i variable ...

Beka Westberg

unread,
Apr 10, 2020, 5:40:57 PM4/10/20
to Blockly
Ok that's definitely weird. Do you have somewhere where we could pull the code to give it a look?

If not, I think you're gonna need to do some deeper digging to figure out where that var is comming from and stop it from happening. I'd recommend moving to uncompressed mode, so that you can add logging to core functions. Then I might start by adding a stack trace (console.trace) or breakpoint to the variableMap.createVariable function. If a variable is getting added to your workspace, it should have to go through there.

If you don't get anything from that then I'm really having trouble thinking of what it could be.

Best of luck!
--Beka
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

Sabrina K

unread,
Apr 10, 2020, 6:04:19 PM4/10/20
to Blockly


/**
 * @fileoverview Struct blocks for Blockly.
 */
'use strict';


Blockly.Blocks['structs_is'] = {
  init: function() {
    this.appendValueInput("INPUT").setCheck(null).appendField("(")
        .appendField(new Blockly.FieldVariable("", null, ['Struct'], 'Struct'), "VAR")
        .appendField("?");
    this.appendDummyInput().appendField(")");
      
    this.setInputsInline(true);
    this.setOutput(true, "Boolean");
    this.setStyle("struct_blocks");
    this.setTooltip(Blockly.Msg['STRUCTS_IS_TOOLTIP']);
  }
};

Blockly.Blocks['structs_field_get'] = {
  init: function() {
    this.appendValueInput("INPUT").setCheck(null).appendField("(")
        .appendField(new Blockly.FieldVariable("", null, ['Struct-Field'], 'Struct-Field'), "VAR");
    this.appendDummyInput().appendField(")");
      
    this.setInputsInline(true);
    this.setOutput(true, null);
    this.setStyle("struct_blocks");
    this.setTooltip(Blockly.Msg['STRUCTS_FIELD_GET_TOOLTIP']);
  }
};


Blockly.Blocks['structs_make'] = {
  init: function() {
      
    this.appendDummyInput()
        .appendField("(make-")
        .appendField(new Blockly.FieldVariable("", null, ['Struct'], 'Struct'), "VAR");
    
    this.itemCount_ = 0;
    this.updateShape_();
    
    this.setInputsInline(true);
    this.setOutput(true, "Struct");
    this.setStyle("struct_blocks");
    this.setTooltip(Blockly.Msg['STRUCTS_MAKE_TOOLTIP']);
    this.setEditable(false);
  },
    
    
  /**
   * Create XML to represent struct fields.
   * @return {!Element} XML storage element.
   * @this {Blockly.Block}
   */
  mutationToDom: function() {
    var container = Blockly.utils.xml.createElement('mutation');
    container.setAttribute('items', this.itemCount_);
    return container;
  },
  /**
   * Parse XML to restore the struct fields.
   * @param {!Element} xmlElement XML storage element.
   * @this {Blockly.Block}
   */
  domToMutation: function(xmlElement) {
    this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
    this.updateShape_();
  },
    
  /**
   * Modify this block to have the correct number of fields.
   * @private
   * @this {Blockly.Block}
   */
  updateShape_: function() {
    this.removeInput('END', true);
      
    // Add fields
    for (var i = 0; i < this.itemCount_; i++) {
        
      if (!this.getInput('FIELD' + i)) {
        this.appendValueInput('FIELD' + i);
      }
    }
    
    this.appendDummyInput('END').appendField(')');
  }
};






html:

  
  <script>  
    
    // using a Fixed Size Workspace
    var workspace = Blockly.inject('blocklyDiv',
        {media: '../../media/',
         toolbox: document.getElementById('toolbox')});
      
    workspace.registerToolboxCategoryCallback('STRUCT', Blockly.Variables.flyoutStructs);  
      

    function onVariableCreate(event) {
        if (event.type == Blockly.Events.VAR_CREATE &&
            event.varType == 'Struct') {
            alert('new struct var')
            workspace.removeChangeListener(onVariableCreate);
        }
    }
    workspace.addChangeListener(onVariableCreate);  
      
 



// added in variables.js in core
Blockly.Variables.STRUCTS = [];

Blockly.Variables.updateStructs = function(name, val) {
    
  Blockly.Variables.STRUCTS.push([name, val]);
  console.log(Blockly.Variables.STRUCTS);
};


/**
 * Handles "Create Struct" button
 * It will prompt the user for struct-name + field-names, including re-prompts if a name
 * is already in use among the workspace's variables.
 *
 * @param {!Blockly.Workspace} workspace The workspace on which to create the
 *     struct.
 * @param {function(?string=)=} opt_callback A callback. It will be passed an
 *     acceptable new variable name, or null if change is to be aborted (cancel
 *     button), or undefined if an existing variable was chosen.
 * @param {string=} opt_type The type of the variable like 'int', 'string', or
 *     ''. This will default to '', which is a specific type.
 */
Blockly.Variables.createStructButtonHandler = function(
    workspace, opt_callback, opt_type) {
  var type = opt_type || 'Struct';
    
  // This function needs to be named so it can be called recursively.
  var promptAndCheckWithAlert = function(defaultName) {
    Blockly.Variables.promptStructName(Blockly.Msg['NEW_STRUCT_TITLE'], defaultName,
        function(text) {
          if (text) {
              
            // prompt struct-properties: struct-name:field-name1,field-name2,...
            var input = text.replace(/\s+/g, "");
            var struct_list = input.split(":");
            var struct_name = struct_list[0];
            var field_list = [];
              if (struct_list.length > 1) { // struct with fields
                for (var i = 0; i < field_list.length; i++) {
                    
                  var field_name = struct_name + "-" + field_list[i];
                  workspace.createVariable(field_name, 'Struct-Field');
                }
              }
                
              if (opt_callback) {
                opt_callback(text);
              }
            }
          } else {
            // User canceled prompt.
            if (opt_callback) {
              opt_callback(null);
            }
          }
        });
  };
  promptAndCheckWithAlert('');
};


/**
 * Prompt the user for a new struct name.
 * @param {string} promptText The string of the prompt.
 * @param {string} defaultText The default value to show in the prompt's field.
 * @param {function(?string)} callback A callback. It will return the new
 *     variable name, or null if the user picked something illegal.
 */
Blockly.Variables.promptStructName = function(promptText, defaultText, callback) {
  Blockly.prompt(promptText, defaultText, function(newVar) {
    // Merge runs of whitespace.  Strip leading and trailing whitespace.
    // Beyond this, all names are legal.
    if (newVar) {
      newVar = newVar.replace(/[\s+&\\#()$~%'"*<>{}=!§]/g,'').trim();
      if (newVar == Blockly.Msg['RENAME_STRUCT'] ||
          newVar == Blockly.Msg['NEW_STRUCT']) {
        // Ok, not ALL names are legal...
        newVar = null;
      }
    }
    callback(newVar);
  });
};

    
/**
 * Construct the blocks required by the flyout for the structs category.
 * @param {!Blockly.Workspace} workspace The workspace this flyout is for.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.Variables.flyoutStructBlocks = function(workspace) {
    
  var struct_fields = workspace.getVariablesOfType('Struct-Field');
  var structs = workspace.getVariablesOfType('Struct');
  console.log(structs);
    
  var STRUCTS = Blockly.Variables.STRUCTS;
  var struct_num = Blockly.Variables.STRUCTS.length;
  var xmlList = [];
       
  if (struct_num > 0) {
      
    if (Blockly.Blocks['structs_is']) {
        
      var block = Blockly.utils.xml.createElement('block');
      var name = STRUCTS[struct_num - 1][0]; // most recent struct
      var gap = 24;
          
      var blockText = '<xml>' +
                          '<block type="structs_is" gap="' + gap + '">' +
                          '<field name="VAR" variabletype="Struct">' + name + '</field>' +
                          '</block>' +
                          '</xml>';
          
      var block = Blockly.Xml.textToDom(blockText).firstChild;
      xmlList.push(block);

    }
      
    if (Blockly.Blocks['structs_make']) {
        
      for (var i = 0; i < struct_num; i++) {
     
        var block = Blockly.utils.xml.createElement('block');
        var name = STRUCTS[i][0];
        var fields_num = STRUCTS[i][1].length;
        var gap = 24;
          
        var blockText = '<xml>' +
                          '<block type="structs_make" gap="' + gap + '">' +
                          '<field name="VAR" variabletype="Struct">' + name + '</field>' +
                          '<mutation items="' + fields_num + '"></mutation> ' +
                          '</block>' +
                          '</xml>';
          
        var block = Blockly.Xml.textToDom(blockText).firstChild;
        xmlList.push(block);
      }
    }
  }
    
  if (struct_fields.length > 0) {
      
    if (Blockly.Blocks['structs_field_get']) {
      
      var block = Blockly.utils.xml.createElement('block');
      block.setAttribute('type', 'structs_field_get');
      block.setAttribute('gap', 24);
      block.appendChild(Blockly.Variables.generateVariableFieldDom(
          struct_fields[struct_fields.length - 1]));
      xmlList.push(block);
    }
  }
    
  return xmlList;
};


/**
 * Construct the elements (blocks and button) required by the flyout for the
 * structs category.
 * @param {!Blockly.Workspace} workspace The workspace containing variables.
 * @return {!Array.<!Element>} Array of XML elements.
 */
Blockly.Variables.flyoutStructs = function(workspace) {
  var xmlList = [];
  var button = document.createElement('button');
  button.setAttribute('text', '%{BKY_NEW_STRUCT}');
  button.setAttribute('callbackKey', 'CREATE_STRUCT');

  workspace.registerButtonCallback('CREATE_STRUCT', function(button) {
    Blockly.Variables.createStructButtonHandler(button.getTargetWorkspace(), null, 'Struct');
  });

  xmlList.push(button);

  var blockList = Blockly.Variables.flyoutStructBlocks(workspace);
  xmlList = xmlList.concat(blockList);
  return xmlList;
};




couldn't attach files so here's the code (hope this form is okay)
thank you so much for your support!

i will try the console.trace

Beka Westberg

unread,
Apr 10, 2020, 6:49:29 PM4/10/20
to Blockly
Thanks for the code! It worked really well! ...To well actually, I couldn't get the bug to reproduce :/

I had to make a few changes to the createStructButtonHandler function to get it to run:
Blockly.Variables.createStructButtonHandler = function(
    workspace
, opt_callback, opt_type) {
 
var type = opt_type || 'Struct';


 
// This function needs to be named so it can be called recursively.
 
var promptAndCheckWithAlert = function(defaultName) {
   
Blockly.Variables.promptStructName(Blockly.Msg['NEW_STRUCT_TITLE'], defaultName,
       
function(text) {

         
if (!text) {

           
// User canceled prompt.
           
if (opt_callback) {
              opt_callback
(null);
           
}
         
}



         
// prompt struct-properties: struct-name:field-name1,field-name2,...

         
var input = text.replace(/\s+/g, "");
         
var struct_list = input.split(":");
         
var struct_name = struct_list[0];
         
var field_list = [];

         
if (struct_list.length > 1) { // struct with fields

            field_list
= struct_list[1].split(',');
         
}



         
Blockly.Variables.updateStructs(struct_name, field_list);
          workspace
.createVariable(struct_name, type);

         
for (var i = 0; i < field_list.length; i++) {
           
var field_name = struct_name + "-" + field_list[i];
            workspace
.createVariable(field_name, 'Struct-Field');
         
}


         
if (opt_callback) {
            opt_callback
(text);
         
}

       
});
 
};
  promptAndCheckWithAlert
('');
};


But that's the only thing I changed. Idk what else to tell ya, sorry I can't be more help :/

--Beka

Sabrina K

unread,
Apr 11, 2020, 3:20:38 AM4/11/20
to Blockly
that's really frustrating because I still get the erroneous variables :/

Sabrina K

unread,
Apr 11, 2020, 5:08:52 AM4/11/20
to Blockly

trace.png



here's my trace - created new struct 'ball' and dragged the make-block in the workspace. Next time I open the struct category there's the new i variable 

any suggestions? 


Am Samstag, 11. April 2020 00:49:29 UTC+2 schrieb Beka Westberg:
Message has been deleted

Sabrina K

unread,
Apr 12, 2020, 4:39:10 AM4/12/20
to Blockly

I could fix it!
but I had to remove the mutator :/

any other suggestion how to get the make-block dynamic?
or is there a solution for mutators in flyout variable blocks?

thanks for your help!



Am Samstag, 11. April 2020 00:49:29 UTC+2 schrieb Beka Westberg:

Coda Highland

unread,
Apr 12, 2020, 9:57:00 AM4/12/20
to blo...@googlegroups.com
Hmm... Maybe the problem is in the initial creation of the block, before the mutator is applied? Can you get away with initializing it without any variables (or using existing variables if there are any)?

/s/ Adam

--
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/a9b0888a-b782-45a9-9b95-4c419885caa0%40googlegroups.com.

Sabrina K

unread,
Apr 12, 2020, 10:52:40 AM4/12/20
to Blockly
I tried a few variants for the mutator but none worked :/
Guess I'll stay with the static make-blocks with fixed inputs
To unsubscribe from this group and stop receiving emails from it, send an email to blo...@googlegroups.com.

Coda Highland

unread,
Apr 12, 2020, 11:30:46 AM4/12/20
to blo...@googlegroups.com
No, I mean, don't change the mutator itself. The mutator is probably fine. Right now I'm eyeballing your init() method and the flow that Blockly goes through before it gets to the mutator. I suspect the variables are being created earlier.

/s/ Adam

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/10700a71-2744-42c5-8b32-33a5bc41d7bd%40googlegroups.com.

Sabrina K

unread,
Apr 12, 2020, 1:34:40 PM4/12/20
to Blockly

the init method in the structs_make block? I wouldn't know what to change about that.
The variable fields should be fine, too - they're the same as in the other blocks that work and need to be in the init() method and in the xml block text

Coda Highland

unread,
Apr 12, 2020, 2:23:38 PM4/12/20
to blo...@googlegroups.com
It's sort of a wild guess, but I blame this line:

.appendField(new Blockly.FieldVariable("", null, ['Struct'], 'Struct'), "VAR"); 

According to the docs, passing null to the first parameter (but I'm guessing it's any falsy value, which the empty string counts as) means that "a unique variable name will be generated." "i" "j" "k" feels very much like what such a behavior would do. I'm wondering if your problem would go away if you passed an existing variable name instead of "".

On a tangential note, I'm going to have to look a bit closer into this for my own use. I hadn't really thought of it before, but this strategy might work a lot better than what I'm currently trying to do for building classes in my own project. (Or I might not. I do kind of like the architecture I've built. I'll have to weigh the options.)

/s/ Adam

 

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/f6b94a3c-c36f-4453-90b9-098eb9ccd3f1%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages