Reference Source
public class | source

ContextMenu

A customizable HTML context menu.

Overview

  • A pure JavaScript, lightweight context menu
  • Dynamically configure menu items
  • Dynamically enable or disable items
  • Supports cascading sub-menus
  • Configure custom style with custom CSS (see examples above)

Usage

In the example below we'll create a ContextMenu that pops up whenever we right-click on an Entity within our Scene.

First, we'll create the ContextMenu, configuring it with a list of menu items.

Each item has:

  • a title for the item,
  • a doAction() callback to fire when the item's title is clicked, and
  • an optional getEnabled() callback that indicates if the item should enabled in the menu or not.


The getEnabled() callbacks are invoked whenever the menu is shown. When an item's getEnabled() callback returns true, then the item is enabled and clickable. When it returns false, then the item is disabled and cannot be clicked. An item without a getEnabled() callback is always enabled and clickable.

Note how the doAction() and getEnabled() callbacks accept a context object. That must be set on the ContextMenu before we're able to we show it. The context object can be anything. In this example, we'll use the context object to provide the callbacks with the Entity that we right-clicked.

We'll also initially enable the ContextMenu.

[Run this example]

const canvasContextMenu = new ContextMenu({

   enabled: true,

   items: [
      [
         {
            title: "Hide Object",
            getEnabled: (context) => {
                return context.entity.visible; // Can't hide entity if already hidden
            },
            doAction: function (context) {
                context.entity.visible = false;
            }
         }
      ],
      [
         {
            title: "Select Object",
            getEnabled: (context) => {
                return (!context.entity.selected); // Can't select an entity that's already selected
            },
            doAction: function (context) {
                context.entity.selected = true;
            }
         }
      ],
      [
         {
            title: "X-Ray Object",
            getEnabled: (context) => {
                return (!context.entity.xrayed); // Can't X-ray an entity that's already X-rayed
            },
            doAction: (context) => {
                context.entity.xrayed = true;
            }
         }
      ]
   ]
});

Next, we'll make the ContextMenu appear whenever we right-click on an Entity. Whenever we right-click on the canvas, we'll attempt to pick the Entity at those mouse coordinates. If we succeed, we'll feed the Entity into ContextMenu via the context object, then show the ContextMenu.

From there, each ContextMenu item's getEnabled() callback will be invoked (if provided), to determine if the item should be enabled. If we click an item, its doAction() callback will be invoked with our context object.

Remember that we must set the context on our ContextMenu before we show it, otherwise it will log an error to the console, and ignore our attempt to show it.

viewer.scene.canvas.canvas.oncontextmenu = (e) => { // Right-clicked on the canvas

    if (!objectContextMenu.enabled) {
        return;
    }

    var hit = viewer.scene.pick({ // Try to pick an Entity at the coordinates
        canvasPos: [e.pageX, e.pageY]
    });

    if (hit) { // Picked an Entity

        objectContextMenu.context = { // Feed entity to ContextMenu
            entity: hit.entity
        };

        objectContextMenu.show(e.pageX, e.pageY); // Show the ContextMenu
    }

    e.preventDefault();
});

Note how we only show the ContextMenu if it's enabled. We can use that mechanism to switch between multiple ContextMenu instances depending on what we clicked.

Dynamic Item Titles

To make an item dynamically regenerate its title text whenever we show the ContextMenu, provide its title with a getTitle() callback. The callback will fire each time you show ContextMenu, which will dynamically set the item title text.

In the example below, we'll create a simple ContextMenu that allows us to toggle the selection of an object via its first item, which changes text depending on whether we are selecting or deselecting the object.

[Run an example]

const canvasContextMenu = new ContextMenu({

   enabled: true,

   items: [
      [
         {
             getTitle: (context) => {
                 return (!context.entity.selected) ? "Select" : "Undo Select";
             },
             doAction: function (context) {
                 context.entity.selected = !context.entity.selected;
             }
         },
         {
             title: "Clear Selection",
             getEnabled: function (context) {
                 return (context.viewer.scene.numSelectedObjects > 0);
             },
             doAction: function (context) {
                 context.viewer.scene.setObjectsSelected(context.viewer.scene.selectedObjectIds, false);
             }
         }
      ]
   ]
});

Each menu item can optionally have a sub-menu, which will appear when we hover over the item.

In the example below, we'll create a much simpler ContextMenu that has only one item, called "Effects", which will open a cascading sub-menu whenever we hover over that item.

Note that our "Effects" item has no doAction callback, because an item with a sub-menu performs no action of its own.

[Run this example]

const canvasContextMenu = new ContextMenu({
    items: [ // Top level items
        [
            {
                getTitle: (context) => {
                    return "Effects";
                },

                items: [ // Sub-menu
                    [
                        {
                            getTitle: (context) => {
                                return (!context.entity.visible) ? "Show" : "Hide";
                            },
                            doAction: function (context) {
                                context.entity.visible = !context.entity.visible;
                            }
                        },
                        {
                            getTitle: (context) => {
                                return (!context.entity.selected) ? "Select" : "Undo Select";
                            },
                            doAction: function (context) {
                                context.entity.selected = !context.entity.selected;
                            }
                        },
                        {
                            getTitle: (context) => {
                                return (!context.entity.highlighted) ? "Highlight" : "Undo Highlight";
                            },
                            doAction: function (context) {
                                context.entity.highlighted = !context.entity.highlighted;
                            }
                        }
                    ]
                ]
            }
         ]
     ]
});

Constructor Summary

Public Constructor
public

Creates a ContextMenu.

Member Summary

Public Members
public set

Sets the ContextMenu context.

public get

Gets the ContextMenu context.

public set

Sets whether this ContextMenu is enabled.

public get

Gets whether this ContextMenu is enabled.

public set

Sets the ContextMenu items.

public get

Gets the ContextMenu items.

public get

Gets whether this ContextMenu is currently shown or not.

Method Summary

Public Methods
public

Destroys this ContextMenu.

public

hide()

Hides this ContextMenu.

public

show(pageX: Number, pageY: Number)

Shows this ContextMenu at the given page coordinates.

Public Constructors

public constructor(cfg: Object) source

Creates a ContextMenu.

The ContextMenu will be initially hidden.

Params:

NameTypeAttributeDescription
cfg Object
  • optional

ContextMenu configuration.

cfg.items Object
  • optional

The context menu items. These can also be dynamically set on ContextMenu#items. See the class documentation for an example.

cfg.context Object
  • optional

The context, which is passed into the item callbacks. This can also be dynamically set on ContextMenu#context. This must be set before calling ContextMenu#show.

cfg.enabled Boolean
  • optional
  • default: true

Whether this ContextMenu is initially enabled. ContextMenu#show does nothing while this is false.

Public Members

public set context: Object source

Sets the ContextMenu context.

The context can be any object that you need to be provides to the callbacks configured on ContextMenu#items.

This must be set before calling ContextMenu#show.

public get context: Object source

Gets the ContextMenu context.

public set enabled: Boolean source

Sets whether this ContextMenu is enabled.

Hides the menu when disabling.

public get enabled: Boolean source

Gets whether this ContextMenu is enabled.

ContextMenu#show does nothing while this is false.

public set items: Object[] source

Sets the ContextMenu items.

These can be updated dynamically at any time.

See class documentation for an example.

public get items: Object[] source

Gets the ContextMenu items.

public get shown: Boolean: * source

Gets whether this ContextMenu is currently shown or not.

Return:

Boolean

Whether this ContextMenu is shown.

Public Methods

public destroy() source

Destroys this ContextMenu.

public hide() source

Hides this ContextMenu.

public show(pageX: Number, pageY: Number) source

Shows this ContextMenu at the given page coordinates.

Does nothing when ContextMenu#enabled is false.

Logs error to console and does nothing if ContextMenu#context has not been set.

Params:

NameTypeAttributeDescription
pageX Number

Page X-coordinate.

pageY Number

Page Y-coordinate.