21 Nov 2022
Customizing Viewer Context Menus: Part 2
Couple years ago our colleague Eason blogged about customizing viewer context menus. In this blog post let's take a closer look at the specific capabilities of this customization.
As a quick reminder, context menus in the viewer are customized by registering so-called context menu callbacks.
viewer.registerContextMenuCallback('your-custom-callback-id', function (menu, status) {
// Customize your menu here
});
// ... and later
viewer.unregisterContextMenuCallback('your-custom-callback-id');
One important thing to note here is that the callbacks are executed every time the context menu is about to open. This allows you to add dynamic behavior to your customized menu, for example, hiding/showing specific menu entries, or changing their text or appearance based on the current state of the viewer.
In their simplest form, context menu callbacks can simply add a new menu entry like so:
viewer.registerContextMenuCallback('your-custom-callback-id', function (menu, status) {
menu.push({
title: 'Say Hello World',
target: function () { alert('Hello World!'); }
});
});
The menu
argument passed to the callback is an array of JavaScript objects representing individual menu entries. Each object can define the following properties:
title
- menu entry titletarget
- can be one of the following:
- function to execute when the menu entry is clicked
- an array of JavaScript objects representing nested menu entries
icon
- CSS class name for an optional icon in front of the menu entry titleshortcut
- menu entry keyboard shortcut
- note that this will only display the shortcut letter, not handle the actual keyboard events
divider
- boolean flag turning this menu entry into a divider
viewer.registerContextMenuCallback('your-custom-callback-id', function (menu, status) {
menu.push({
title: 'My Actions',
target: [
{
title: 'Say Hello World',
icon: 'adsk-icon-pan',
shortcut: 'h',
target: function () { alert('Hello World!'); }
},
{
divider: true
},
{
title: 'Say Goodbye',
icon: 'adsk-icon-plane-x',
target: function () { alert('Goodbye!'); }
}
]
});
});
If you want to reuse some of the viewer icons as we did in the example above, please refer to https://forge.autodesk.com/blog/what-icons-are-provided-viewer-stylesheet for more details.
Finally, the status
argument passed to your context menu callback is a JavaScript object with useful information about the current status of the viewer and the event that triggered the context menu itself. These are the properties available in the object:
event
- browser event that requested the context menunumSelected
- number of objects selected in the viewerhasSelected
- boolean flag indicating whether any objects are selectedhasVisible
- boolean flag indicating whether any of the selected objects are visiblehasHidden
- boolean flag indicating whether any of the selected objects are hiddencanvasX
,canvasY
- X and Y coordinate of the mouse event that requested the context menu
Of course, you can always use the viewer APIs if this status object does not provide information you need:
viewer.registerContextMenuCallback('your-custom-callback-id', function (menu, status) {
if (status.hasSelected) {
menu.push({
title: `Color ${status.numSelected} Selected Elements`,
target: function () {
const dbids = viewer.getSelection();
const color = new THREE.Vector4(1, 0, 0, 0.5);
for (const dbid of dbids) {
viewer.setThemingColor(dbid, color, undefined, true);
}
viewer.clearSelection();
}
});
}
menu.push({
title: 'Clear Custom Color',
target: function () {
viewer.clearThemingColors();
}
});
});