Skip to main content

Adding a Category

Adding the category to scratch-blocks

Let's move the alert block to a Dialogs category.

src/blocks

First, create a file called dialogs.ts in src/blocks.

At the top of the file put:

scratch-blocks/src/blocks/dialogs.ts
import * as Blockly from "blockly/core";

Move your block from looks.ts to dialogs.ts (leaving looks.ts like it was originally).

scratch-blocks/src/blocks/dialogs.ts
Blockly.Blocks['dialogs_alert'] = {
init: function(this: Blockly.Block){
this.jsonInit({
message0: Blockly.Msg.DIALOGS_ALERT,
args0: [
{
type: "input_value",
name: "MESSAGE",
},
],
extensions: ["colours_dialogs", "shape_statement"],
});
},
};

The block messages

First, move your block's message to a new section (to keep the file organised) and update the message to match the one in the block definition.

Second, under the "Category labels" section, add one for your new category.

scratch-blocks/msg/messages.js
// Category labels
Blockly.Msg.CATEGORY_DIALOGS = 'Dialogs';
Blockly.Msg.CATEGORY_MOTION = 'Motion';
Blockly.Msg.CATEGORY_LOOKS = 'Looks';
Blockly.Msg.CATEGORY_SOUND = 'Sound';
Blockly.Msg.CATEGORY_EVENTS = 'Events';
Blockly.Msg.CATEGORY_CONTROL = 'Control';
Blockly.Msg.CATEGORY_SENSING = 'Sensing';
Blockly.Msg.CATEGORY_OPERATORS = 'Operators';
Blockly.Msg.CATEGORY_VARIABLES = 'Variables';
Blockly.Msg.CATEGORY_MYBLOCKS = 'My Blocks';

The block category

In src/blocks/vertical_extensions.ts, find categoryNames inside the registerAll() function and add your category to it.

scratch-blocks/src/blocks/vertical_extensions.ts
const categoryNames = [
"dialogs",
"control",
"data",
"data_lists",
"sounds",
"motion",
"looks",
"event",
"sensing",
"pen",
"operators",
"more",
];

Additionally, add an import for your category in index.ts with the other imports.

scratch-blocks/src/index.ts
import * as Blockly from "blockly/core";
import "./blocks/colour";
import "./blocks/math";
import "./blocks/matrix";
import "./blocks/note";
import "./blocks/text";
import "./blocks/vertical_extensions";
import "./blocks/dialogs";
import "./blocks/control";
import "./blocks/data";
import "./blocks/event";
import "./blocks/looks";
import "./blocks/motion";
import "./blocks/operators";
import "./blocks/procedures";
import "./blocks/sensing";
import "./blocks/sound";
import * as scratchBlocksUtils from "./scratch_blocks_utils";
// ...

Adding the category to scratch-gui

The block colour

In scratch-gui at src/lib/settings/color-mode/default/index.js, add your colour to the blockColors object.

scratch-editor/packages/scratch-gui/src/lib/settings/color-mode/default/index.js
// This object is passed directly to Blockly, hence the colour* fields need to
// be named exactly as they are, including the UK spelling of "colour".
const blockColors = {
dialogs: {
colourPrimary: '#60a05c',
colourSecondary: '#4c8249',
colourTertiary: '#31542f',
colourQuaternary: '#31542f'
},
motion: {
colourPrimary: '#4C97FF',
colourSecondary: '#4280D7',
colourTertiary: '#3373CC',
colourQuaternary: '#3373CC'
},

The colours are these:

ColourHex CodeExample
Primary#60a05c
Secondary#4c8249
Tertiary#31542f
Quaternary#31542f
tip

If you want, you can also add the category to the dark, high-constrast or your own custom themes themes.

Adding the category to the toolbox

In src/lib/make-toolbox-xml.js, remove your changes from the looks category and add the following:

scratch-gui/src/lib/make-toolbox-xml.js
const dialogs = function (isInitialSetup, isStage, targetId, colors) {
const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!');
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
return `
<category name="${ScratchBlocks.ScratchMsgs.translate(
'CATEGORY_DIALOGS',
'Dialogs'
)}" toolboxitemid="dialogs" colour="${
colors.colourPrimary
}" secondaryColour="${colors.colourTertiary}">
<block type="dialogs_alert">
<value name="MESSAGE">
<shadow type="text">
<field name="TEXT">${hello}</field>
</shadow>
</value>
</block>
${categorySeparator}
</category>
`;
};

At the bottom of the file, there is a function called makeToolboxXml, make the highlighted changes to it:

scratch-gui/src/lib/make-toolbox-xml.js
const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categoriesXML = [],
costumeName = '', backdropName = '', soundName = '', colors = defaultColors) {
// ...

categoriesXML = categoriesXML.slice();
const moveCategory = categoryId => {
// ...
};
const dialogsXML = moveCategory('dialogs') || dialogs(isInitialSetup, isStage, targetId, colors.dialogs);
const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId, colors.motion);
const looksXML = moveCategory('looks') ||
looks(isInitialSetup, isStage, targetId, costumeName, backdropName, colors.looks);
// ...

const everything = [
xmlOpen,
dialogsXML, gap,
motionXML, gap,
looksXML, gap,
// ...
myBlocksXML
];

for (const extensionCategory of categoriesXML) {
everything.push(gap, extensionCategory.xml);
}

everything.push(xmlClose);
return everything.join('\n');
};

Adding the category to scratch-vm.

First, remove the changes from scratch3_looks.js.

In the src/blocks folder, add a tutorialmod_dialogs.js file.

scratch-vm/src/blocks/tutorialmod_dialogs.js
class TutorialModDialogsBlocks {
constructor (runtime){
/**
* The runtime instantiating this block package.
* @type {Runtime}
*/
this.runtime = runtime;
}

/**
* Retrieve the block primitives implemented by this package.
* @return {object.<string, Function>} Mapping of opcode to Function.
*/
getPrimitives () {
return {
dialogs_alert: this.alertBlock
};
}

alertBlock (args, util) {
// eslint-disable-next-line
alert(args.MESSAGE);
}
}

module.exports = TutorialModDialogsBlocks;

And finally, in src/engine/runtime.js, add your category's file to defaultBlockPackages

scratch-vm/src/engine/runtime.js
const defaultBlockPackages = {
tutorialmod_dialogs: require('../blocks/tutorialmod_dialogs'),
scratch3_control: require('../blocks/scratch3_control'),
scratch3_event: require('../blocks/scratch3_event'),
scratch3_looks: require('../blocks/scratch3_looks'),
scratch3_motion: require('../blocks/scratch3_motion'),
scratch3_operators: require('../blocks/scratch3_operators'),
scratch3_sound: require('../blocks/scratch3_sound'),
scratch3_sensing: require('../blocks/scratch3_sensing'),
scratch3_data: require('../blocks/scratch3_data'),
scratch3_procedures: require('../blocks/scratch3_procedures')
};

Make scratch-vm understand how to load the category from save

In scratch-vm's src/serialization/sb3.js file, find the CORE_CATEGORIES array and add your category to it.

scratch-vm/src/serialization/sb3.js
const CORE_EXTENSIONS = [
'argument',
'colour',
'control',
'data',
'event',
'looks',
'math',
'motion',
'operator',
'procedures',
'sensing',
'sound',
'dialogs'
];

Completed Files

ComponentFileDownload
scratch-blockssrc/blocks/dialogs.tsdialogs.ts
scratch-blockssrc/blocks/vertical_extensions.tsvertical_extensions.ts
scratch-blockssrc/index.tsindex.ts
scratch-blocksmsg/messages.jsmessages.js
scratch-guisrc/lib/make-toolbox-xml.jsmake-toolbox-xml.js
scratch-guisrc/lib/themes/default/index.jsindex.js
scratch-vmsrc/blocks/tutorialmod_dialogs.jstutorialmod_dialogs.js
scratch-vmsrc/engine/runtime.jsruntime.js
scratch-vmsrc/serialization/sb3.jssb3.js

Scratch commit hashes at the time of this tutorial
scratch-editor:     810ac12eaa1a1eb19d05fcb37f486ad1c4c56314
scratch-blocks: bb2f7ce297e2552767b531c212b18ee4f046e92d