Toolbox

The toolbox is the side menu from where the user may create new blocks. It is contained inside the WorkbenchViewController.

Example of a toolbox

There are two ways to populate a Toolbox:

  • XML (Recommended)
  • Programmatically

Populate a Toolbox via XML

Here is a minimal example of declaring a toolbox in XML:

<xml>
  <block type="controls_if"></block>
  <block type="controls_repeat_ext"></block>
</xml>

If this XML was included in your project as toolbox.xml, here's how you would create a Toolbox from that file:

Swift

// Create a workbench editor
let workbenchViewController = WorkbenchViewController(style: .defaultStyle)

// Load the workbench's block factory with the default blocks
let blockFactory = workbenchViewController.blockFactory
blockFactory.load(fromDefaultFiles: .allDefault)

do {
  if let toolboxFile = Bundle.main.path(forResource: "toolbox", ofType: "xml") {
    // Load the XML from `toolbox.xml`
    let toolboxXML = try String(contentsOfFile: toolboxFile, encoding: String.Encoding.utf8)

    // Create a toolbox from the XML, using the blockFactory to create blocks declared in the XML
    let toolbox = try Toolbox.makeToolbox(xmlString: toolboxXML, factory: blockFactory)

    // Load the toolbox into the workbench
    try workbenchViewController.loadToolbox(toolbox)
  } else {
    print("Could not load 'toolbox.xml'")
  }
} catch let error {
  // Handle error
  print("Error populating toolbox: \(error)")
}

Objective-C

// Create a workbench editor
BKYWorkbenchViewController *workbenchViewController =
  [[BKYWorkbenchViewController alloc] initWithStyle:BKYWorkbenchViewControllerStyleDefaultStyle];

// Load the workbench's block factory with the default blocks
BKYBlockFactory *blockFactory = workbenchViewController.blockFactory;
[blockFactory loadFromDefaultFiles:BKYBlockJSONFileAllDefault];

// Load the XML from `toolbox.xml`
NSError *error = nil;
NSString *toolboxFile = [[NSBundle mainBundle] pathForResource:@"toolbox" ofType:@"xml"];
NSString *toolboxXML =
  [NSString stringWithContentsOfFile:toolboxFile encoding:NSUTF8StringEncoding error:&error];
if (error) {
  NSLog(@"Could not load 'toolbox.xml'");
  return;
}

// Create a toolbox from the XML, using the blockFactory to create blocks declared in the XML
BKYToolbox *toolbox =
  [BKYToolbox makeToolboxWithXmlString:toolboxXML factory:blockFactory error:&error];
if (error) {
  NSLog(@"Error creating toolbox from XML: %@", error);
  return;
}

// Load the toolbox into the workbench
[workbenchViewController loadToolbox:toolbox error:&error];
if (error) {
  NSLog(@"Error loading toolbox into the workbench: %@", error);
  return;
}

This produces:

Example of a toolbox without categories.

Generate XML via Blockly Developer Tools

If you don't like typing XML manually, we recommend that you check out Blockly Developer Tools. With it, you can construct a toolbox and automatically generate its toolbox XML using a visual interface.

Categories

The blocks in the toolbox may be organized in categories. Here is an example of a toolbox with two categories ('Control' and 'Loops'):

<xml>
  <category name="Control" colour="#f44336">
    <block type="controls_if"></block>
  </category>
  <category name="Loops" colour="#43A047" icon="icon_loops">
    <block type="controls_repeat_ext"></block>
    <block type="controls_for"></block>
  </category>
</xml>

Below is the resulting toolbox:

Example of toolbox categories with one tab open.

Each category can be assigned two optional attributes:

  • colour (note the British spelling): Specifies either a RGB string ("#RRGGBB") or a number (0-360) defining its hue, which is used as the category's tab colour.
  • icon: Specifies an image to use for the category (instead of the text name). For an image inside an asset catalog, specify the name of the image asset. For an image inside the main bundle, specify the path relative to the main bundle. (In the example above, icon_loops is specifying the name of an image defined inside of an asset catalog.)

Block Groups

The XML may contain customized blocks, or groups of blocks. Here are four blocks:

  1. A simple logic_boolean block:
    A block for klzzwxh:0014.
  2. A math_number block that has been modified to display the number 42 instead of the default of 0:
    A block for the number klzzwxh:0018.
  3. A controls_for block that has three math_number blocks connected to it:
    A for loop block with three inline numbers.
  4. A math_arithmetic block that has two math_number shadow blocks connected to it:
    An addition block using shadow number blocks.

Here is the code to generate these four blocks in a toolbox:

<xml>
  <block type="logic_boolean"></block>

  <block type="math_number">
    <field name="NUM">42</field>
  </block>

  <block type="controls_for">
    <value name="FROM">
      <block type="math_number">
        <field name="NUM">1</field>
      </block>
    </value>
    <value name="TO">
      <block type="math_number">
        <field name="NUM">10</field>
      </block>
    </value>
    <value name="BY">
      <block type="math_number">
        <field name="NUM">1</field>
      </block>
    </value>
  </block>

  <block type="math_arithmetic">
    <field name="OP">ADD</field>
    <value name="A">
      <shadow type="math_number">
        <field name="NUM">1</field>
      </shadow>
    </value>
    <value name="B">
      <shadow type="math_number">
        <field name="NUM">1</field>
      </shadow>
    </value>
  </block>
</xml>

Shadow blocks are placeholder blocks that perform several functions:

  • They indicate the default values for their parent block.
  • They allow users to type values directly without needing to fetch a number or string block.
  • Unlike a regular block, they get replaced if the user drops a block on top of them.
  • They inform the user of the type of value expected.

Separators

Adding a <sep></sep> tag between any two blocks will create a separator of 24 units (relative to Blockly's coordinate system). This separation may be changed using an optional gap attribute, which will replace the default gap.

<xml>
  <block type="math_number"></block>
  <sep></sep>
  <block type="math_arithmetic">
    <field name="OP">ADD</field>
  </block>
  <sep gap="8"></sep>
  <block type="math_arithmetic">
    <field name="OP">MINUS</field>
  </block>
</xml>

Adjusting the gaps between blocks allows one to create logical groups of blocks in the toolbox.

Example of toolbox with separators

Example XML with All Default Blocks

Here is an example toolbox_basic.xml that contains all default blocks available in Blockly.

Populate a Toolbox Programmatically

If you'd prefer to avoid XML altogether, the following example shows how to create a multi-category toolbox programmatically:

Swift

// Create a workbench editor
let workbenchViewController = WorkbenchViewController(style: .defaultStyle)

// Load the workbench's block factory with the default blocks
let blockFactory = workbenchViewController.blockFactory
blockFactory.load(fromDefaultFiles: .allDefault)

do {
  // Create a new toolbox
  let toolbox = Toolbox()

  // Add a "Control" category with one block
  let controlCategory = toolbox.addCategory(name: "Control", color: UIColor.red)
  try controlCategory.addBlockTree(
    try blockFactory.makeBlock(name: "controls_if"))

  // Add a "Loops" category with two blocks
  let iconLoops = UIImage(named: "icon_loops")
  let loopsColor = ColorHelper.makeColor(hue: 120)
  let loopsCategory = toolbox.addCategory(name: "Loops", color: loopsColor, icon: iconLoops)
  try loopsCategory.addBlockTree(
    try blockFactory.makeBlock(name: "controls_whileUntil"))
  try loopsCategory.addBlockTree(
    try blockFactory.makeBlock(name: "controls_for"))

  // Load the toolbox into the workbench
  try workbenchViewController.loadToolbox(toolbox)
} catch let error {
  print("Error populating the toolbox: \(error)")
  return
}

Objective-C

- (void)populateWorkbenchWithToolbox {
  // Create a workbench editor
  BKYWorkbenchViewController *workbenchViewController =
    [[BKYWorkbenchViewController alloc] initWithStyle:BKYWorkbenchViewControllerStyleDefaultStyle];

  // Load the workbench's block factory with the default blocks
  BKYBlockFactory *blockFactory = workbenchViewController.blockFactory;
  [blockFactory loadFromDefaultFiles:BKYBlockJSONFileAllDefault];

  // Create a new toolbox
  NSError *error = nil;
  BKYToolbox *toolbox = [[BKYToolbox alloc] init];

  // Add a "Control" category with one block
  BKYToolboxCategory *controlCategory =
    [toolbox addCategoryWithName:@"Control" color:[UIColor redColor]];
  [self addBlockWithName:@"controls_if" fromFactory:blockFactory toCategory:controlCategory];

  // Add a "Loops" category with two blocks
  UIImage *iconLoops = [UIImage imageNamed:@"icon_loops"];
  UIColor *loopsColor = [BKYColorHelper makeColorWithHue:120];
  BKYToolboxCategory *loopsCategory =
    [toolbox addCategoryWithName:@"Loops" color: loopsColor icon: iconLoops];
  [self addBlockWithName:@"controls_whileUntil" fromFactory:blockFactory toCategory:loopsCategory];
  [self addBlockWithName:@"controls_for" fromFactory:blockFactory toCategory:loopsCategory];

  // Load the toolbox into the workbench
  [workbenchViewController loadToolbox:toolbox error:&error];
  if (error) {
    NSLog(@"Error loading the toolbox: %@", error);
    return;
  }

  // Add workbench to view controller (not included here for brevity)
}

/** Helper method for adding a block to a category. */
- (void)addBlockWithName:(NSString*)blockName
     fromFactory:(BKYBlockFactory*)blockFactory
      toCategory:(BKYToolboxCategory*)category {
  NSError* error = nil;

  // Create block
  BKYBlock *block = [blockFactory makeBlockWithName:blockName error:&error];
  if (error) {
    NSLog(@"Error creating '%@' block: %@", blockName, error);
    return;
  }

  // Add block to category
  [category addBlockTree:block error:&error];
  if (error) {
    NSLog(@"Error adding '%@' block to category '%@': %@", blockName, [category name], error);
    return;
  }
}