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 andTabStacks
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.
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.