Windows
Overview
Available since io.Connect Desktop 10.5 & @interopio/desktop 6.23.0
⚠️ Note that the new Layouts API is designed to replace the legacy Layouts API accessible via the
io.layoutsobject. The legacy Layouts API is still supported, but will be entirely removed in a future release. It's highly recommended to migrate to the new Layouts API. When Layout sharing is enabled, the legacy Layouts API is unreliable and you must use the new Layouts API instead.
The new Layouts API is accessible via the io.layouts.v2 object. All its methods are asynchronous and Layouts are identified by their IDs.
Configuration
The new Layouts API doesn't require any configuration. It's available automatically when the Layouts API is enabled in the @interopio/desktop library (the default) and works with all Layout stores, regardless of whether Layout sharing is enabled.
The io.layouts.v2 object is undefined in platforms that don't support the new Layouts API.
Layout IDs
Each Layout is described by a Layout object.
When Layouts are stored in io.Manager, the ID of a Layout is assigned by io.Manager and doesn't change when the Layout is renamed. In other Layout stores, the ID of a Layout is formed by its name and type (e.g., "My Layout (Global)"), so it changes when the Layout is renamed.
Methods that operate on a specific Layout accept as a first argument an object with an id property. You can pass the Layout object itself, or an object containing only the Layout ID:
const layout = await io.layouts.v2.get({ id: "My Layout (Global)" });
// Both calls restore the same Layout.
await io.layouts.v2.restore(layout);
await io.layouts.v2.restore({ id: layout.id });Layout Operations
Retrieving Layouts
To retrieve a specific Layout, use the get() method and pass an object with the ID of the Layout. The method resolves with a Layout object, or with null if the Layout doesn't exist:
const layout = await io.layouts.v2.get({ id: "my-layout-id" });To retrieve multiple Layouts, use the getMany() method. It accepts an optional filter object with type and name properties, which you can use to filter the Layouts by type and by name (exact match). If you don't pass a filter, the method resolves with all available Layouts:
// Retrieving all Layouts.
const allLayouts = await io.layouts.v2.getMany();
// Retrieving all Global Layouts.
const globalLayouts = await io.layouts.v2.getMany({ type: "Global" });By default, the returned Layout objects don't contain the components, the context, and the access information of the Layouts. To include them, use the includeComponents, includeContext, and includeAccessInfo properties of the GetLayoutOptions and LayoutFilter objects passed to the get() and getMany() methods.
The following example demonstrates retrieving all Global Layouts with their context and finding a Layout by a value in its context:
const options = { type: "Global", includeContext: true };
const layouts = await io.layouts.v2.getMany(options);
const layout = layouts.find(layout => layout.context?.clientId === 42);Current Global Layout
To retrieve the currently active Global Layout, use the get() method of the io.layouts.v2.current object. It resolves with a CurrentLayoutState object describing the state of the current Global Layout, or with null if there isn't a currently active Global Layout:
const currentLayoutState = await io.layouts.v2.current.get();
if (currentLayoutState) {
const { layout, status, modified } = currentLayoutState;
console.log(`Current Global Layout: "${layout.name}", status: ${status}, modified: ${modified}.`);
}Save
To save a Layout, use the save() method and pass a SaveLayoutOptions object with a required name property. The method resolves with the saved Layout object:
const options = {
name: "My Layout",
type: "Global",
context: { clientId: 42 }
};
const layout = await io.layouts.v2.save(options);If you don't specify an id in the options object and Layout sharing is disabled, a Layout with the same name and type will be overwritten if it exists, or a new Layout will be created. When Layout sharing is enabled, a new Layout will be created if you don't specify an id (unless you already have a private Layout with the same name and type, in which case the operation will fail), so you must specify the ID of the Layout you want to overwrite.
Restore
To restore a Layout, use the restore() method and pass the Layout to restore. As a second optional argument, you can pass a RestoreOptions object. The method resolves with a RestoreResult object containing the ID of the restore session, which you can use to reset the restored Layout:
const layout = await io.layouts.v2.get({ id: "my-layout-id" });
const options = {
// Specify whether to close all running apps before restoring the Layout.
closeRunningInstances: false
};
const result = await io.layouts.v2.restore(layout, options);⚠️ Note that saving, and especially restoring, a Global Layout may be a taxing operation depending mainly on how many apps there are in the Layout, how heavy they are, and how much context is attached to the Layout. If you need a quicker way to switch between Global Layouts, see the Hibernate & Resume section.
Restore History
To retrieve information about the currently active Global Layout, as well as about any previously active Global Layouts that may still have running instances of their participating apps, use the getRestoreHistory() method. It resolves with a RestoreLayoutHistory object, or with null if no Layout has been restored in the current session:
const history = await io.layouts.v2.getRestoreHistory();
history?.layouts.forEach(({ layout, trackingId, instances }) => {
console.log(`Layout "${layout.name}" (restore session ID: ${trackingId}) has ${instances.length} app instances.`);
});Retrieving Layout Contents
To retrieve the contents of a Global Layout or a Workspace Layout, use the getContents() method and pass the Layout as a required first argument:
const layout = await io.layouts.v2.get({ id: "my-layout-id" });
const {
appComponents,
workspaceComponents,
workspaceWindows
} = await io.layouts.v2.getContents(layout);
// List all apps in the Layout.
console.log("Apps:", appComponents);
// List all Workspaces App instances in the Layout.
console.log("Workspaces App instances:", workspaceComponents);
// List all windows participating in all Workspaces.
console.log("Workspace windows:", workspaceWindows);When used for a Global Layout, the returned object contains all components participating in the Layout: individual app instances (including all Workspaces App instances), all Workspace instances hosted in all Workspaces App instances, and all windows participating in all Workspaces. When used for a Workspace Layout, the returned object contains only the windows participating in the Workspace.
To specify whether the Workspace contents in a Global Layout will be retrieved in their original state described in their Workspace Layouts, or in the state in which they were saved in the Global Layout, use the getWorkspacesByReference property of the optional object passed as a second argument:
const contents = await io.layouts.v2.getContents(layout, { getWorkspacesByReference: true });Hibernate & Resume
Hibernating and resuming provides a quicker way to save and restore Global Layouts. The hibernation operation saves the Layout (therefore the original Global Layout will be overwritten) and enables you to resume (restore) the Layout more quickly by preserving the participating app instances and hiding them from the user. If you have heavy apps in a Layout and they take a lot of time to load, you may choose to hibernate the Layout when the user switches to another Layout. Resuming the Layout later will save the time usually needed for loading the heavy apps, but at the expense of higher memory consumption on the user machine.
⚠️ Note that in order for the hibernation operation to be most beneficial, you should prevent your app instances from closing when being hibernated. For more details, see the Developers > Configuration > System > App Settings > Hibernation and Developers > Configuration > Application > Hibernation sections.
To hibernate a Layout, use the hibernate() method and pass the Layout to hibernate as a required first argument. As a second optional argument, you can pass an object with context and metadata properties. The method resolves with the hibernated Layout object:
const options = {
context: { clientId: 42 },
metadata: { hibernatedBy: "my-app" }
};
const hibernatedLayout = await io.layouts.v2.hibernate(layout, options);To resume a hibernated Layout, use the resume() method and pass the Layout to resume as a required first argument. As a second optional argument, you can pass an object with context and cleanUp properties:
const options = {
context: { clientId: 42 },
// Will close any already running app instances before resuming the Layout.
cleanUp: true
};
const result = await io.layouts.v2.resume(layout, options);
console.log(`Layout resumed ${result.status === "Success" ? "successfully" : "unsuccessfully"}.`);Rename
To rename a Layout, use the rename() method and pass the Layout to rename and the new name for it:
await io.layouts.v2.rename(layout, "My New Layout");Remove
To remove a Layout, use the remove() method and pass the Layout to remove:
await io.layouts.v2.remove(layout);Export & Import
To export Layouts, use the export() method. It accepts an optional Layout type as an argument. If you don't specify a Layout type, all Layouts will be exported:
const globalLayouts = await io.layouts.v2.export("Global");To import Layouts, use the import() method. Pass the list of Layout objects to import and optionally specify an import mode. If set to "replace", all existing Layouts will be removed and will be replaced by the imported ones. If set to "merge", the imported Layouts will be added to the existing ones, replacing any Layouts with conflicting names. The method resolves with an object with an imported property holding the successfully imported Layouts and an errors property holding the names and types of the Layouts that couldn't be imported:
const { imported, errors } = await io.layouts.v2.import(globalLayouts, "merge");Update Metadata
To update the metadata of a Layout, use the updateMetadata() method. Pass the Layout and the new metadata as required arguments. By default, the new metadata replaces the existing one. To merge the new metadata with the existing one, pass an object with a mode property set to "merge" as a third argument:
const metadata = { clientId: 42 };
await io.layouts.v2.updateMetadata(layout, metadata, { mode: "merge" });Reset
Resetting a Layout programmatically enables you to quickly revert a restored Global Layout to its default state, without the user having to manually start missing app instances or move and resize windows. Already running instances that are part of the Layout will be reused.
⚠️ Note that Workspaces Apps aren't included in the reset Layout operation.
To reset a Layout, use the reset() method and pass an object with a trackingId property holding the ID of the restore session. You can obtain this ID from the result of the restore() method, from the restore history, or from the onRestored() event. As a second optional argument, you can pass a ResetOptions object:
const { trackingId } = await io.layouts.v2.restore(layout);
const options = {
// Will exclude hidden apps from the reset operation.
visibleOnly: true,
// Will create missing app instances.
createMissing: true,
// Will preserve the current state of the app instances (window context and selected Channel).
resetState: false
};
const result = await io.layouts.v2.reset({ trackingId }, options);Layout Sharing
When Layout sharing is enabled, the advancedLayoutsSupported property of the API is true:
const isLayoutSharingSupported = io.layouts.v2.advancedLayoutsSupported;To retrieve the information about the owner of a Layout and the users and groups with access to it, set the includeAccessInfo property of the object passed to the get() or the getMany() method to true. The access information is available in the accessInfo property of the returned Layout object, which holds a LayoutAccessInfo object.
To change the access to a Layout, use the updateAccess() method. Pass the Layout and an object with accessLevel and accessEntities properties. The owner of the Layout is preserved:
const isLayoutSharingSupported = io.layouts.v2.advancedLayoutsSupported;
if (isLayoutSharingSupported) {
const currentLayoutState = await io.layouts.v2.current.get();
const layoutId = { id: currentLayoutState.layout.id };
const accessInfo = {
accessLevel: "shared",
accessEntities: [
{ entityType: "group", entityName: "Traders", accessLevel: "view" },
{ entityType: "user", entityName: "john.doe", accessLevel: "edit" }
]
};
await io.layouts.v2.updateAccess(layoutId, accessInfo);
}You can also specify the access to a Layout when saving it by using the accessInfo property of the object passed to the save() method.
⚠️ Note that the
updateAccess()method and theaccessInfoproperty of the object passed to thesave()method are available only when Layout sharing is enabled. Otherwise, the operations will throw an error.
Save & Update Context
When a Layout is saved, apps can store context data in it. When the Layout is restored, the context data is also restored and returned to the apps.
⚠️ Note that saving large volumes of custom data as window context (e.g., thousands of lines of table data) can lead to significant delays when saving a Layout. If your app needs to save large amounts of data, you may store IDs, indices, etc., as context data, save the actual data to a database, and when you restore the Layout, fetch the data using the data IDs saved as window context.
Saving Context Data
To save context data, apps can subscribe for Layout save requests by using the onSaveRequested() method. The callback will receive as an argument an object containing the Layout name, type, and context. The callback must return an object with a windowContext property holding the context to save:
const saveRequestHandler = (requestInfo) => {
const { layoutType } = requestInfo;
if (layoutType === "Global") {
return { windowContext: { gridWidth: 420 } };
}
return { windowContext: { gridWidth: 42 } };
};
const unsubscribe = await io.layouts.v2.onSaveRequested(saveRequestHandler);After the Layout has been restored, the saved context data will be available in the window context:
const windowContext = await io.windows.my().getContext();
const gridWidth = windowContext.gridWidth;Updating App Context
To update the context that will be saved for the current app, use the updateComponentContext() method. If the app participates in the currently active Global Layout or in a Workspace Layout, its context will be updated in that Layout. Otherwise, the context will be updated in the App Default Layout of the app:
const context = { clientId: 42 };
await io.layouts.v2.updateComponentContext(context);Default Global Layout
io.Connect Desktop allows you to specify a default Global Layout that will be automatically loaded on start. Use the io.layouts.v2.default object to get, set, and clear the default Global Layout programmatically.
To retrieve the default Global Layout, use the get() method. It resolves with a Layout object, or with null if no default Global Layout has been set:
const defaultLayout = await io.layouts.v2.default.get();To set a default Global Layout, use the set() method and pass the Layout:
await io.layouts.v2.default.set(layout);To clear the default Global Layout, use the clear() method:
await io.layouts.v2.default.clear();Events
The new Layouts API provides methods for reacting to various Layout events. All methods resolve with an unsubscribe function that you can use to stop receiving notifications about the respective event. The Layout objects received by the event handlers don't contain the components, the context, and the access information of the Layouts. To retrieve them, use the get() method.
Added
To get notified when a Layout is added, use the onAdded() method. The handler is invoked for all existing Layouts when you subscribe:
const handler = layout => console.log(`Layout "${layout.name}" was added.`);
const unsubscribe = await io.layouts.v2.onAdded(handler);Removed
To get notified when a Layout is removed, use the onRemoved() method:
const handler = layout => console.log(`Layout "${layout.name}" was removed.`);
const unsubscribe = await io.layouts.v2.onRemoved(handler);Changed
To get notified when a Layout is changed (e.g., it's saved or its metadata is updated), use the onChanged() method:
const handler = layout => console.log(`Layout "${layout.name}" was changed.`);
const unsubscribe = await io.layouts.v2.onChanged(handler);Renamed
To get notified when a Layout is renamed, use the onRenamed() method:
const handler = layout => console.log(`Layout "${layout.id}" was renamed to "${layout.name}".`);
const unsubscribe = await io.layouts.v2.onRenamed(handler);Restored
To get notified when a Layout is restored, use the onRestored() method. The Layout object received by the handler has a trackingId property holding the ID of the restore session, which you can use to reset the Layout:
const handler = layout => console.log(`Layout "${layout.name}" was restored. Restore session ID: ${layout.trackingId}.`);
const unsubscribe = await io.layouts.v2.onRestored(handler);Hibernate & Resume
To get notified when a Layout is hibernated, use the onHibernated() method:
const handler = layout => console.log(`Layout "${layout.name}" was hibernated.`);
const unsubscribe = await io.layouts.v2.onHibernated(handler);To get notified when a hibernated Layout is resumed, use the onResumed() method. Resuming a Layout doesn't raise the onRestored() event:
const handler = layout => console.log(`Layout "${layout.name}" was resumed.`);
const unsubscribe = await io.layouts.v2.onResumed(handler);Current Global Layout
To get notified when a different Global Layout becomes the current one, or when there is no longer a current Global Layout, use the onChanged() method of the io.layouts.v2.current object. The handler receives a CurrentLayoutState object, or null:
const handler = (state) => {
if (state) {
console.log(`Current Global Layout: "${state.layout.name}".`);
} else {
console.log("There is no current Global Layout.");
}
};
const unsubscribe = await io.layouts.v2.current.onChanged(handler);To get notified when the current Global Layout is modified (e.g., an app has been started or closed), use the onModified() method of the io.layouts.v2.current object. The handler receives a LayoutModifiedEvent object:
const handler = ({ layout }) => console.log(`Current Global Layout "${layout.name}" was modified.`);
const unsubscribe = await io.layouts.v2.current.onModified(handler);Default Global Layout
To get notified when the default Global Layout is set or cleared, use the onChanged() method of the io.layouts.v2.default object. The handler receives the default Global Layout, or null:
const handler = layout => console.log(`Default Global Layout: ${layout ? `"${layout.name}"` : "none"}.`);
const unsubscribe = await io.layouts.v2.default.onChanged(handler);