Shadow Blocks Pt. 3: Using Customized Fields

Published 8/5/19

In the previous part of this series we started talking about ways to customize your shadows with things like default values and tooltips. In this part we’re going to kick it up a notch and talk about customized fields!


Go to the previous post: Shadow Blocks Pt 2

Using customized fields

Creating a shadow with a customized field is probably the hardest way to customize shadows. To do it you need to know a lot about how to customize fields (the default ones and/or your custom ones) and have a good grasp on where they’re useful. But customized fields are also the most powerful way to customize shadows.


Let’s take another look at the ‘Turn’ block from a previous post:

The shadow uses a customized angle field to make its function more intuitive.


Out of all the default fields I think these ones are the most useful for custom shadows:

  1. Angle fields (with some core edits).
  2. Number fields.
  3. Color fields.
  4. Text input fields.

With color and text fields being distant competitors.

Angle fields

I think angle fields are the most useful for customization, because with a few little parameter changes you can change how your user thinks about the angle they are inputting.

Blockly.defineBlocksWithJsonArray([
  {
    "type": "example_turn",
    "message0": "turn %1",
    "args0": [
      {
        "type": "input_value",
        "name": "DEGREES"
      }
    ],
    "inputsInline": true,
    "previousStatement": null,
    "nextStatement": null,
    "style": "example_blocks"
  },
  {
    "type": "example_turn_right",
    "message0": "%1",
    "args0": [
      {
        "type": "field_angle",
        "name": "DEGREES",
        "angle": 0,
        "offset": 90,
        "clockwise": true
      }
    ],
    "output": "Number",
    "style": "example_blocks"
  },
  {
    "type": "example_turn_left",
    "message0": "%1",
    "args0": [
      {
        "type": "field_angle",
        "name": "DEGREES",
        "angle": 0,
        "offset": 90,
        "clockwise": false
      }
    ],
    "output": "Number",
    "style": "example_blocks"
  },
  {
    "type": "example_sector_parent",
    "message0": "create sector %1",
    "args0": [
      {
        "type": "input_value",
        "name": "DEGREES"
      }
    ],
    "inputsInline": true,
    "previousStatement": null,
    "nextStatement": null,
    "style": "example_blocks"
  },
  {
    "type": "example_sector",
    "message0": "%1",
    "args0": [
      {
        "type": "field_angle",
        "name": "DEGREES",
        "angle": 0,
        "offset": 45,
        "clockwise": true
      }
    ],
    "output": "Number",
    "style": "example_blocks"
  },
  {
    "type": "example_start_angle_parent",
    "message0": "set start %1",
    "args0": [
      {
        "type": "input_value",
        "name": "DEGREES"
      }
    ],
    "inputsInline": true,
    "previousStatement": null,
    "nextStatement": null,
    "style": "example_blocks"
  },
  {
    "type": "example_start_angle",
    "message0": "%1",
    "args0": [
      {
        "type": "field_angle",
        "name": "DEGREES",
        "angle": 0,
        "wrap": 180
      }
    ],
    "output": "Number",
    "style": "example_blocks"
  },
]);
<block type="example_turn">
  <value name="DEGREES">
    <shadow type="example_turn_right"></shadow>
  </value>
</block>
<block type="example_turn" y="60">
  <value name="DEGREES">
    <shadow type="example_turn_left"></shadow>
  </value>
</block>
<block type="example_sector_parent" y="110">
  <value name="DEGREES">
    <shadow type="example_sector"></shadow>
  </value>
</block>
<block type="example_start_angle_parent" y="160">
  <value name="DEGREES">
    <shadow type="example_start_angle"></shadow>
  </value>
</block>

As you can see, each of the above blocks gives you a slightly different impression of what you’re doing.


Sadly as of 2.20190722.0 you can only set the “parameters” I mentioned above globally. But if you download the below version of the angle field, replace, and rebuild you’ll be able to set them on a per-field basis (I’m hoping to add this to the core soon).


Download the better angle field. Note: it’s based on the pre 2.20190722.0 fields API (but post #1594), so that it’s backwards compatible.

Number fields

Number fields also come with some great customization through the setConstraints API. This API allows you to set the minimum value, the maximum value, and the precision (i.e. what the value gets rounded to).


Take this move by pixels block for example:

Blockly.defineBlocksWithJsonArray([
  {
    "type": "example_move_pixels",
    "message0": "move by %1 pixels",
    "args0": [
      {
        "type": "input_value",
        "name": "PIXELS"
      }
    ],
    "inputsInline": true,
    "previousStatement": null,
    "nextStatement": null,
    "style": "example_blocks"
  },
  {
    "type": "example_pixels",
    "message0": "%1",
    "args0": [
      {
        "type": "field_number",
        "name": "NUMBER",
        "min": 1,
        "precision": 1
      }
    ],
    "output": "Number",
    "style": "example_blocks"
  }
]);
<block type="example_move_pixels">
  <value name="PIXELS">
    <shadow type="example_pixels"></shadow>
  </value>
</block>

In this case we only want whole numbers, and we can easily achieve that.


The only problem with this is that while the shadow is constrained the input is not. This means you will have to add extra checks to your generator, to make sure you’re only getting the values you want.

Colour fields

It may seem odd to customize the options of a colour shadow when the user could just override it with a colour block of their own, but I think there is a genuine use case. Imagine, for example, that you were creating some sort of content management system using Blockly.

Blockly.defineBlocksWithJsonArray([
  {
    "type": "example_set_background",
    "message0": "set background %1",
    "args0": [
      {
        "type": "input_value",
        "name": "BACKGROUND"
      }
    ],
    "inputsInline": true,
    "previousStatement": null,
    "nextStatement": null,
    "style": "example_blocks"
  }
]);

Blockly.Blocks["example_colours"] = {
  init: function() {
    // As of 2.20190722.0 the colour field can only
    // be customized using JavaScript.
    var colourField = new Blockly.FieldColour('#2EBBE6')
        .setColours(
          ['#2EBBE6', '#E62E68', '#E6AE2E', '#24B377'],
          ['blue', 'red', 'yellow', 'green']
        )
        .setColumns(4);

    this.appendDummyInput()
      .appendField(colourField, 'COLOUR');
    this.setOutput(true, 'Colour');
    this.setStyle('example_blocks');
  }
};
<block type="example_set_background">
  <value name="BACKGROUND">
    <shadow type="example_colours"></shadow>
  </value>
</block>

Giving the users of this system access to a pallet of company colours, while still allowing them to customize if needed, could be very useful.

Text input fields

Text inputs also have a teeny-tiny API that may be useful, vis. the setSpellcheck API.

Blockly.defineBlocksWithJsonArray([
  {
    "type": "example_say",
    "message0": "say %1",
    "args0": [
      {
        "type": "input_value",
        "name": "TEXT"
      }
    ],
    "inputsInline": true,
    "previousStatement": null,
    "nextStatement": null,
    "style": "example_blocks"
  },
  {
    "type": "example_spellcheck_false",
    "message0": "%1",
    "args0": [
      {
        "type": "field_input",
        "name": "TEXT",
        "text": "helo!",
        "spellcheck": false
      }
    ],
    "output": "String",
    "style": "example_blocks"
  },
  {
    "type": "example_spellcheck_true",
    "message0": "%1",
    "args0": [
      {
        "type": "field_input",
        "name": "TEXT",
        "text": "helo!"
      }
    ],
    "output": "String",
    "style": "example_blocks"
  },
]);
<block type="example_say">
  <value name="TEXT">
    <shadow type="example_spellcheck_false"></shadow>
  </value>
</block>
<block type="example_say" y="60">
  <value name="TEXT">
    <shadow type="example_spellcheck_true"></shadow>
  </value>
</block>

By creating one little block (or downloading the files below) you can mix and match which values are spell checked and which are not.

Custom shadows download

I thought it might be useful to have a collection of tiny shadow blocks to play around with, so I went ahead and created one! The download below includes block definitions and javascript generators (but not generators for other languages). Just remember, there’s no replacement for customizing yourself!


Download the custom blocks.

Conclusion

Shadow blocks are an important part of constructing a block, and they can be made even better with a little bit of TLC. Adding a default value, or a customized field can make the difference between an ok block and a great one. I hope this series of posts made you think a little differently about how you use your shadows :D