Skip to content

Windows, buttons, and menus

Parameters for windows, iframes, buttons, and import and export menu actions in editorAPI.

The methods on this page create UI inside BDEngine. The examples assume that the editor UI is ready. When a plugin loads together with the editor, wait for the bde:started event. Import and export actions can be registered earlier: they appear once the menus have been created.

createWindow

editorAPI.createWindow(
name = 'Window',
name_id = 'default',
icon = 'icon-layout-grid',
width = 1100,
height = 550,
minWidth = 500,
minHeight = 400,
closeFunc = () => {}
);

Synchronously creates and opens a movable window. Returns a window instance, not a DOM element. Add content to its container.

Parameter Meaning
name Window title. Use your own string without untrusted HTML
name_id Window identifier. Use a prefix for your plugin, such as my-plugin-preview
icon Icon class. Also accepts { type: 'image', src, fallback }, where fallback is a fallback icon class
width, height Initial size in CSS pixels
minWidth, minHeight Minimum size when the user resizes the window
closeFunc Function with no arguments, called when the close icon is clicked

closeFunc must be a function; otherwise, the call throws an API error. Reusing the same name_id does not reuse an existing window: keep your own reference to the instance.

const panel = editorAPI.createWindow(
'Preview', 'my-plugin-preview', 'icon-eye',
700, 450, 400, 300
);
const text = document.createElement('p');
text.textContent = 'The plugin preview will appear here.';
panel.container.append(text);

The returned object provides these members:

Member Action
container DOM container for the plugin’s content
headerAdd DOM container for additional elements in the window header
focus() Brings the window to the front; restores it if minimized
minimize() / restore() Minimizes / restores the window
setName(name) Changes the title; the string is interpreted as HTML
setIcon(icon, updateManager = true) Changes the icon and updates the window manager by default
close() Starts closing the window and removing its DOM

These methods are synchronous and do not return a result. After the closing animation, windowDiv and container become null. Create a new window instance to open it again. Calling close() directly does not call closeFunc. If both closing paths need cleanup, invoke it explicitly and make it safe to run more than once.

createWindowIFrame

editorAPI.createWindowIFrame(
url = 'https://www.block-display.com/',
name = 'Window', name_id = 'default', icon = 'icon-layout-grid',
width = 1100, height = 550, minWidth = 500, minHeight = 400,
closeFunc = () => {}, iframeAttributes = {}
);

Creates the same kind of window with an iframe inside. Returns a window object with an additional iframe property; the padding of container is set to 0. url specifies the page address. The other window parameters have the same meaning.

iframeAttributes is an object of HTML attributes for the iframe. Values of null, undefined, and false are skipped; true creates an empty attribute; other values are converted to strings. Attributes are set before src is assigned.

const help = editorAPI.createWindowIFrame(
'https://docs.bde.gg/en/plugins/',
'Plugin documentation', 'my-plugin-help', 'icon-book-open',
900, 600, 400, 300, () => {},
{ title: 'BDEngine Manual', referrerpolicy: 'no-referrer' }
);

Browser restrictions still apply: a site may prohibit embedding, and access to the DOM of a page on another origin is restricted. The plugin chooses sandbox, allow, and any other required attributes; they are not added automatically.

createModalWindow

const modal = editorAPI.createModalWindow(name = 'Window');

Synchronously creates a hidden modal window and returns a ModalGUI object. name is the title; add content elements to modal.container. The window is already in the DOM, but displaying it requires a separate call.

const modal = editorAPI.createModalWindow('Plugin settings');
const close = document.createElement('button');
close.type = 'button';
close.textContent = 'Done';
close.addEventListener('click', () => modal.hideModal());
modal.container.append(close);
modal.showModal();

showModal

modal.showModal() displays an existing window. Returns undefined. The method hides other modal components in editor.gui, releases interactive camera input, and exits Pointer Lock when necessary. It is not a shared manager for every modal created by third-party plugins: manage switching between your own windows yourself.

hideModal

modal.hideModal() hides the window while retaining its DOM, content, and event handlers. Returns undefined. You can show the window again without recreating it. Clicking the backdrop calls the same method. In immersion mode, hiding may be deferred while the editor is dragging an object.

addButtonDockMenu

editorAPI.addButtonDockMenu(
icon = 'icon-box', name = 'New button', func = () => {}, sound = true
);

Adds an item to the dock menu and synchronously returns an HTMLAnchorElement. icon is the icon class, name is the item label, and func is a callback with no arguments. After the callback, the menu closes and a sound plays. The label is inserted into markup, so use trusted text.

const button = editorAPI.addButtonDockMenu(
'icon-settings', 'Plugin settings', () => modal.showModal()
);
// When the item is no longer needed:
// button.remove();

The current wrapper always passes sound = true: setting this parameter to false does not currently disable sound for this method. An asynchronous func is not awaited and does not prevent repeated clicks. For a long-running operation, manage its state yourself, for example with the disabled class on the returned element.

addButtonTool

editorAPI.addButtonTool(icon, func, right = false, sound = true);

Adds a button to the left (right = false) or right (right = true) toolbar. Synchronously returns an HTMLButtonElement.

icon is required: one nonempty CSS class string without whitespace. func is required and must be a function; it is called with no arguments. Invalid values throw an API error. sound = false does disable the click sound for this button.

const button = editorAPI.addButtonTool('icon-info', () => {
console.log('Selected objects:', editorAPI.getSelectedObjects().length);
}, true, false);
button.title = 'Number of selected objects';
button.setAttribute('aria-label', button.title);

Use button.disabled to disable the button and button.remove() to remove it. The callback’s Promise is not awaited: the plugin must prevent repeated execution and handle errors from asynchronous operations.

addButtonImportMenu

const action = editorAPI.addButtonImportMenu({
id: 'my-plugin-import',
title: 'Plugin import',
icon: 'icon-upload',
enabled: true,
onClick(event) { /* open a file picker */ }
});

Registers an item in File > Import, after the built-in actions and a separator. Synchronously returns a handle for managing the action. File > Export uses the same contract.

options field Contract
id Required nonempty string. Unique within the import or export menu; use a plugin prefix
title Required nonempty string, displayed as text
icon Optional single CSS class without whitespace; defaults to '', with no icon
onClick Required function. Receives the click event and may return a Promise
enabled Optional boolean. Defaults to true for a new item

Invalid types or missing required fields throw an API error. Registering the same id again updates the existing item’s title, icon, and callback. If enabled is omitted during that update, its current value is retained. The repeated call returns the same handle.

Handle method Return value and action
action.setEnabled(boolean) Changes availability and returns action for chaining
action.remove() Removes the registration and button. Returns true on the first removal, or false if the registration no longer exists
action.setEnabled(false);
action.setEnabled(true);
// action.remove();

The item is disabled while its callback runs, including the entire time spent awaiting a Promise. The menu closes before the callback starts. The callback is invoked before the first await, so a file picker can use the user’s activation. Exceptions and rejected Promises are logged to the console with the action’s context, and the temporary disabled state is then cleared. The plugin is responsible for displaying an error message to the user.

addButtonExportMenu

editorAPI.addButtonExportMenu(options) adds an action to File > Export. Its parameters, return value, replacement by id, and Promise handling are the same as for import. The method itself does not define an export format or retrieve project data.

const exportAction = editorAPI.addButtonExportMenu({
id: 'my-plugin-inspect-export',
title: 'Get JSON for the plugin',
icon: 'icon-download',
async onClick() {
const data = await editorAPI.exportToJSON(false);
if (data === false) return;
console.log(data);
}
});

For details about the result and changes to the export window, see exportToJSON.

State and repeated execution

A file menu action is reused by id. Dock buttons, toolbar buttons, regular windows, and modal windows are created anew on every call. Keep them in your plugin’s state to avoid duplicating UI when it is opened again.

Removing a button does not remove handlers attached to window or document, nor does it stop intervals, observers, or network requests. Closing a window does not automatically unload the plugin either. Implement your own resource cleanup and check it using Debugging and repeated execution.

Put it into practice