A context menu contains a list of actions that a user can perform on a workspace, block, or workspace comment. The context menu is shown in response to a right-click, or a long press on a touch device.
Context menus are a good place to add actions that the user performs infrequently, such as downloading a screenshot. If you think an action will be used more commonly, you might want to create a more discoverable way to invoke it.
There are separate context menus for workspaces, blocks, and workspace comments, each of which you can customize. You can also customize context menus on a per-block or per-workspace basis.
How context menus work
Blockly has a registry that contains menu item templates. Each template describes how to construct a single item in a context menu. When the user invokes a context menu, Blockly:
Goes through all of the templates in the registry and decides which ones apply to the component on which the context menu was invoked (workspace, block, or workspace comment).
Uses these templates to construct a corresponding array of menu items.
If the context menu was invoked on a workspace or block, checks to see if that workspace or block has a function for customizing the context menu. If so, it passes the array to the function, which can add, delete, or modify elements of the array.
Displays the context menu using the (possibly modified) array of context menu items.
The registry is preloaded with a standard set of templates for constructing context menus for workspaces and blocks. You can add, delete, or modify these templates. Blockly also has predefined templates for items that apply to workspace comments, but you must explicitly add these to the registry.
The RegistryItem interface
All templates in the registry implement the
ContextMenuRegistry.RegistryItem
interface. It contains the
following properties.
ID
The id
property should be a unique string that indicates what your context
menu item does.
const deleteTemplate = {
id: 'deleteElement',
// ...
};
Scope type
The scopeType
property tells Blockly what context the menu item can be shown
in and what component to pass to its displayText
, preconditionFn
, and
callback
functions. Context menus can be scoped to blocks, workspace comments,
and workspaces.
const deleteTemplate = {
// ...
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
// ...
};
If you want a template to apply to multiple scopes, duplicate the template and
redefine the scopeType
property for each scope.
const otherDeleteTemplate = {...deleteTemplate};
otherDeleteTemplate.scopeType = Blockly.ContextMenuRegistry.ScopeType.COMMENT;
Display text
The displayText
is what should be shown to the user as part of the menu item.
The display text can be a string, or HTML, or a function that returns either a
string or HTML.
const deleteTemplate = {
// ...
displayText: 'Delete block',
// ...
};
If you want to display a translation from Blockly.Msg
you need to use a
function. If you try to assign the value directly, the messages may not be
loaded and you'll get a value of undefined
instead.
const deleteTemplate = {
// ...
displayText: () => Blockly.Msg['MY_DELETE_ITEM_TEXT'],
// ...
};
If you use a function, it is also passed a Scope
value which gives
you access to the element the context menu is associated with (e.g. the block or
workspace). You can use this to add information about the element to your
display text.
const deleteTemplate = {
// ...
displayText: (scope) => `Delete ${scope.block.type} block`,
// ...
}
Weight
The weight
determines the order in which context menu items are displayed.
More positive values are displayed lower in the list than less positive values.
(You can imagine that items with higher weights are "heavier" so they sink to
the bottom.)
const deleteTemplate = {
// ...
weight: 10,
// ...
}
Weights for the built-in context menu items go in increasing order starting at 1 and increasing by 1.
Precondition function
In addition to the scopeType
, you can use the preconditionFn
to restrict
when and how a context menu item should be displayed.
It should return one of a set of strings: 'enabled'
, 'disabled'
, or
'hidden'
.
Value | Description | Image |
---|---|---|
enabled | Shows that the item is active. | ![]() |
disabled | Shows that the item is not active. | ![]() |
hidden | Hides the item. |
The preconditionFn
is also passed a Scope
which you can use to determine the
state of your item.
const deleteTemplate = {
// ...
preconditionFn: (scope) => {
if (scope.block.isDeletable()) {
return 'enabled';
}
return 'disabled';
},
// ...
}
Callback function
The callback
property is a function that performs the action of your context
menu item. It is passed a Scope
and a PointerEvent
. The PointerEvent
is
the original event that triggered opening the context menu, not the one that
selected an item. You can use the event to figure out where the click happened.
This lets you, for example, create a new block at the click location.
const deleteTemplate = {
// ...
callback: (scope, e) => {
scope.block.dispose();
},
}
Customize the registry
You can add, delete, or modify templates in the registry.
Add a template
You can add a template to the registry by registering it. You should do this once on page load. It can happen before or after you inject your workspace.
const deleteTemplate = { /* properties from above */ };
Blockly.ContextMenuRegistry.registry.register(deleteTemplate);
Delete a template
You can remove a template from the registry by unregistering it by ID.
Blockly.ContextMenuRegistry.registry.unregister('someID');
The IDs for the default templates are in
contextmenu_items.ts
.
Modify a template
You can modify an existing template by getting the template from the registry and then modifying it in-place.
const template = Blockly.ContextMenuRegistry.registry.getItem('someID');
template?.displayText = 'some other display text';
Disable block context menus
By default, blocks have a context menu that allows users to do things like add comments or duplicate blocks.
You can disable the context menu of an individual block by doing:
block.contextMenu = false;
In the JSON definition of a block type, use the enableContextMenu
key:
{
// ...,
"enableContextMenu": false,
}
Customize context menus per block type or workspace
After Blockly has generated a context menu,
individual blocks or workspaces can customize it. To do this, define a function
as the value of the customContextMenu
property (blocks)
or the configureContextMenu
property (workspaces). The
signatures of these functions are slightly different for blocks and workspaces,
but both accept an array of objects that describe the items in the context menu.
The function can modify the array (add, delete, or modify elements) in place.
The objects passed to blocks implement the
ContextMenuOption
interface or the
LegacyContextMenuOption
interface. The objects
passed to workspaces implement ContextMenuOption
.
ContextMenuOption
has the following properties:
text
is the display text.enabled
is a boolean. When disabled, the item is shown but with grey text.callback
is the function to be called when the item is clicked.scope
is aScope
object, which contains the block or workspace the context menu was invoked on.weight
determines where the item is displayed in the menu.
For example, here is a function that adds a Hello, World!
item to a
workspace's context menu:
workspace.configureContextMenu = function (menuOptions, e) {
const item = {
text: 'Hello, World!',
enabled: true,
callback: function () {
alert('Hello, World!');
},
weight: 100,
};
menuOptions.push(item);
}