Skip to main content

Save users' layouts

Users with data-heavy workflows often need to be able to combine several sources of information to view at a glance. Here™ Core provides the layouts capability to let users do this within a single window, so they can complete their tasks quickly and easily without switching through multiple applications or windows.

📘 Note

This article describes LayoutConfig which saves the current arrangement of views in a window or restores a previous arrangement of views in a window. To understand the Layout APIs that programmatically arrange views and TabStacks in a window, see Arrange views with Layout APIs.

What are layouts?

Layouts allow you to take multiple standalone web applications and compose them together into a single window. What was once a disparate set of tools and apps that people had to arrange on their desktop can now be shown in a single window, bringing those tools together as a cohesive whole. What’s more, these web apps can be run in separate renderer processes or they can share processes. For more information, see Process affinity.

A window with four views in a two-by-two grid

When it comes to representing a layout in code, we can think of layouts as being similar to snapshots.

  • Snapshots are a way to save and restore the positions and configurations of windows on the desktop.

  • Layouts are a way to save and restore the positions and configurations of views in a window.

You can open a window, create views and adjust them to perfection, then save that layout configuration to use in your application.

How to capture a layout

While, technically, you could write the complex JSON of a LayoutConfig by hand, that’s not recommended. Instead, we recommend that you create your views exactly the way you want them, then capture the LayoutConfig data. You can also capture a layout if a user has modified the arrangement of view from the default; you can then reapply that layout as needed by that user.

The function to use when capturing the LayoutConfig data varies depending on whether you want the layout data from the current window, or from another window.

If you want the LayoutConfig data for the current window, use getCurrent or getCurrentSync.

For getCurrent:

const layout = await fin.Platform.Layout.getCurrent();

// Use wrapped instance to control layout, e.g.:
const layoutConfig = await layout.getConfig();

For getCurrentSync:

const layout = fin.Platform.Layout.getCurrentSync();

// Use wrapped instance to control layout, e.g.:
const layoutConfig = await layout.getConfig();

If you want the LayoutConfig data for a window that is not the current window, use wrap or wrapSync.

For wrap:

const layout = await fin.Platform.Layout.wrap({ uuid: fin.me.uuid, name: '<my_window>' });

// Use wrapped instance to control layout, e.g.:
const layoutConfig = await layout.getConfig();

For wrapSync:

const layout = fin.Platform.Layout.wrapSync({ uuid: fin.me.uuid, name: '<my_window>' });

// Use wrapped instance to control layout, e.g.:
const layoutConfig = await layout.getConfig();

How to apply a layout

After you have a LayoutConfig, you can apply it to the layout of views in a window by using the replace function. This replaces the window’s current layout with the new layout. Any views that do not exist in the new layout are destroyed.

let windowIdentity;
if (fin.me.isWindow) {
windowIdentity = fin.me.identity;
} else if (fin.me.isView) {
windowIdentity = (await fin.me.getCurrentWindow()).identity;
} else {
throw new Error('Not running in a platform View or Window');
}

const layout = fin.Platform.Layout.wrapSync(windowIdentity);

const newLayout = {
content: [
{
type: 'stack',
content: [
{
type: 'component',
componentName: 'view',
componentState: {
name: 'new_component_A1',
processAffinity: 'ps_1',
url: 'https://www.example.com'
}
},
{
type: 'component',
componentName: 'view',
componentState: {
name: 'new_component_A2',
url: 'https://cdn.openfin.co/embed-web/chart.html'
}
}
]
}
]
};

layout.replace(newLayout);

An alternative to creating a LayoutConfig and applying it to the window is to use the applyPreset function. The applyPreset function creates a new layout based on preset arrangements, which include columns, grids, rows, and tabs. If one of those preset arrangements works for your use case, it can simplify the task of presenting a layout.

let windowIdentity;
if (fin.me.isWindow) {
windowIdentity = fin.me.identity;
} else if (fin.me.isView) {
windowIdentity = (await fin.me.getCurrentWindow()).identity;
} else {
throw new Error('Not running in a platform View or Window');
}

const layout = fin.Platform.Layout.wrapSync(windowIdentity);
await layout.applyPreset({ presetType: 'grid' });

Configure layout behavior

You can configure various aspects of the layout's behavior, such as whether users are allowed to reorder tabs, resize panes, and close views.

Drag-and-drop behavior

When drag-and-drop is enabled, users can:

  • Drag tab headers to reorder them within a tab strip

  • Drag tab headers to move views between different parts of the layout

  • Drag the splitter between views to resize them

The following Boolean options are available as LayoutOptions.settings to control aspects of drag-and-drop behavior:

  • hasHeaders (default true): Whether views (tabs) have tab headers. If false, the layout is displayed with splitters only.

  • preventSplitterResize (default false): Whether users can resize views by dragging the splitter between them. If true, splitters are not draggable, and the relative proportions of views cannot be changed.

  • reorderEnabled (default true): Whether users can reorder the view tabs in the tab strip by dragging them. Unlike a desktop Here Core environment, dragging is limited to the current window.

Tab controls

The following properties control the behavior of tabs:

On LayoutOptions.settings:

  • showMaximiseIcon (default false): In v40+, whether to show the "maximize icon on the tab header. Clicking the button maximizes the view to fill the current window; when maximized, the button changes to a "restore" icon that reverts the action.

On views:

  • isClosable (default true): Whether the tab header has a close button to enable closing the tab.

Control view title behavior

By default, when the DOM title of the web document changes, through page navigation or updating document.title, the titles of the views it contains are updated to match. You might prefer that views keep their titles, regardless of what happens to the enclosing document's title.

In Here Core v41 and later, the optional titlePriority property of componentState (which is used at view-creation time) controls whether the view title is fixed, or updates based on the document title or URL. It has two possible values:

  • 'document' (default): The view title is the document title from the DOM, if defined; otherwise it displays the view's current URL
  • 'options': The view title retains the value defined when it was created

Custom platform considerations

When using the Here™ Core Platform API, all of the logic and backing for layout operations are already initialized and baked in.

If, however, you have created a custom platform, one additional step is required. You have to call fin.Platform.Layout.init with the initLayoutOptions of the windows that have views controlled by layouts.

For more information on using init in your custom platform, see the init API reference and code examples.