OpenFin

Layouts API

New features available!

Layouts now permits custom window and tab opacity. Learn more here.

OpenFin Layouts delivers window management and layout user experience across the financial desktop. Layouts allows application developers and desktop owners to enable a configurable but cohesive desktop experience to their users. Many of the features work out of the box and full functionality can be quickly achieved, allowing developers on OpenFin to quickly get to market.

API Docs Layouts Config

Using Layouts

Below you will find a tutorial on how to use the OpenFin Layouts service. When you have completed this guide, you will be able to:

  • Enable the layouts service in the application config file to gain access to basic snap and dock, and tabbing functionality
  • Import the client side API into your code
  • Programmatically control snap & dock and tabbing features
  • Use your own custom UI for the tabstrip
  • Create Workspace objects, and rehydrate saved Workspaces
  • Configure the Layouts service to include or exclude certain windows or applications programmatically or in the app config

Prerequisites

OpenFin Layouts acts on windows and applications that have included the service in their application manifest.

To enable the Layouts service, you need to add a single reference to the service in your application config file (app.json). Follow the steps below in the Enabling Layouts section.

Layouts requires users to have monitor scaling of 100%. Please see the versions page for runtime or RVM version dependencies.

OpenFin Layouts cannot currently penetrate through runtimes in security realms that are not connected to the runtime mesh via the ‘enable-mesh’ runtime argument.

License

You agree to the Developer License to download, use, test, or build with any OpenFin Technology. The code in this section is covered by the OpenFin Developer License. If you have any questions, please contact support@openfin.co.

Enabling Layouts

OpenFin Layouts consists of two parts:

  • An OpenFin Desktop Service that delivers the Layouts functionality to OpenFin applications running on the desktop
  • A client-side API that exposes the layouts API to applications that wish to have greater control over the functionality and access to the full Layouts feature set

To enable the Layouts service for an OpenFin application you only need to add a single reference to the service in your application config file (app.json).

"services": [ {"name": "layouts"} ]

The services section of the Application Configuration instructs the Runtime Version Manager to start the layouts service before the application starts. Configuration options can be included in the app config to enable or disable functionality on a feature-by-feature basis for specific windows or applications. Please find further explanation of the lifecycle of OpenFin Desktop Services here.

The following features are now included in the OpenFin Runtime without having to utilize the client-side API:

  • Snap and dock
    • Auto-detect when windows are within the snap region
    • Visually indicate if windows can be snapped or not
    • Auto-pad the windows for a consistent layout
    • Resize windows within a group
    • Undock windows with global hotkey (CTRL+SHIFT+U or CMD+SHIFT+U)

We suggest educating end-users about the undock global hotkey if you do not plan to include any UI to undock.

  • Tabbing
    • Drag & drop windows to create tabbed layouts
    • A default tabstrip UI is provided for tabbed windows

Below is a complete example of a simple application config enabled for Layouts:

{
   "startup_app": {
       "url": "https://openfin.co",
       "uuid": "OpenFin",
       "name": "OpenFin",
       "autoShow": true
   }, 
   "services": [ {"name": "layouts"} ],
   "runtime": {
       "version": "9.61.38.*"
   }
}

The Runtime version specified in the app config file may need to match the version used by the Layouts service. Please find supported Runtime version information on the OpenFin versions page.

Using the Layouts client API

After enabling the service in the app config file as described above, you can utilize the client-side API to further enhance your application’s integration with OpenFin Layouts. The following features require usage of the client-side API:

  • Snap & dock
    • Programmatically undock windows and groups
  • Tabbing
    • Programmatically create and manage tab groups
    • Create custom tab-UI (tabstrip)
  • Saving and restoring layouts
  • Configuration
    • Register / deregister individual windows from Layouts

For a production environment we recommend importing the client-side API into your code as a module by utilizing the npm library openfin-layouts. For a development/test environment, the client-side API could also be accessed from a script tag pointing at our CDN, which adds the functionality in the OpenFinLayouts global. Below is an example of how to do this.

npm CLI command to add openfin-layouts to your package.json:

npm i --save openfin-layouts

HTML script tag pointing at our CDN (not for a production environment):

<script src="https://cdn.openfin.co/services/openfin/layouts/[VERSION]/openfin-layouts.js"></script>

In the following code examples in this tutorial the openfin-layouts library is being imported as a module from npm and we assume usage of a module bundler such as Browserify or Webpack.

Running a specific version

Without further specification, you will get the latest stable version of an OpenFin Desktop Service. We recommend that application providers assume the latest stable version in production as the versioning of the service Provider should be handled by the desktop owner.

For further information, please see the generalized documentation on our OpenFin Desktop Services page.

Layouts features

Snap & Dock

  • Acts only on OpenFin applications that have included the layouts service in the application config
  • Windows snap to all sides of other window or group of windows
  • A window of comparable size will slightly resize to match the size of the target window
  • Visible feedback on where the dragged window will be snapped/resized to target window/group
  • Ability to resize windows together intelligently in a group
  • Windows can be undocked by pressing CTRL+SHIFT+U or CMD+SHIFT+U when the window has focus.
  • On inclusion of the client API, windows and groups can also be undocked programmatically.

Undocking windows

Windows that can be snapped and docked together also need to be undocked. To do this programmatically, the undockWindow API can be used.

//import the client module
import * as layouts from "openfin-layouts"

layouts.snapAndDock.undockWindow();

Undock a different window

layouts.snapAndDock.undockWindow({"uuid":"services-demo",name:"undockWin"});

Undock groups

Snapped groups of windows can also be ungrouped in one step using the undockGroup API.

//import the client module
import * as layouts from "openfin-layouts"

layouts.snapAndDock.undockGroup();

Custom Window and Tab Opacity

Layouts permits custom window and tab opacity. This feature allows a user to differentiate between windows when snapping them together or between tabs when grouping. Windows and tabs can be set at opacity between 0 and 1. Opacity can also be set to "null" to disable opacity. Below is detailed overview of how to set opacity for active and target windows as well as active and target tabs.

To enable this feature for a window, set the opacity under the snap property in your app manifest file. To set the opacity for windows that are being moved, active windows, set the opacity by ratio (0.1 example = 10%) using the activeOpacity property. To set the opacity for windows that the active window will snap to, target windows, set the opacity by ratio (0.1 example = 10%) using the targetOpacity property.

These requirements are relatively similar for setting tab opacity. To enable this feature for tabs, set the opacity under the tab property in your app manifest file and complete the activeOpacity and targetOpacity steps as noted above.

{
    "enabled": true,
    "features": {
        "snap": true,
        "dock": true,
        "tab": true
    },
    "preview": {
        "snap": {
            "activeOpacity": 0.8,
            "targetOpacity": 0.8
        },
        "tab": {
            "activeOpacity": 0.8,
            "targetOpacity": 0.8
        }
    },
    "tabstrip": {
        "url": "",
        "height": 60
    }
}

Workspace Save & Restore

  • Requires usage of the client-side API
  • Acts on all OpenFin applications that have not deregistered from the service, regardless of whether layouts is included in the application config
  • Generate a JSON Workspace object representing the state of the desktop
  • Each application can add custom state data to the Workspace object
  • Rehydrate a Workspace object by launching applications that are not open, positioning and grouping/docking the windows, and implementing custom state

Prerequisites

In order to properly save and restore a workspace, you must:

  • Declare the Layouts service in your application manifest
  • Set the generate handler
  • Set the restore handler
  • Call the ready function

Do not hold logic in your child windows. Each app should set it’s own restore and generate handlers and take care of it’s own responsibilities. Everything should take place in the parent application.

Initialization

When an application starts, handlers need to be setup to initialize the save & restore functionality of the service. After you have setup your handlers, call the workspaces.ready method to notify the service that you have setup your handlers. If you were started by Layouts using the save & restore functionality, the restoreHandler function will be executed with the WorkSpace data. Learn more about the handlers in the sections below.

//import the client module
import * as layouts from "openfin-layouts"

layouts.workspaces.setRestoreHandler(someRestoreFunction);
layouts.workspaces.setGenerateHandler(someGenerateFunction);
layouts.workspaces.ready();

Generating the workspace data

Workspaces are represented in a JSON data structure that captures application, window, grouping, and monitor state. To generate a Workspace object from the current state of the desktop environment, call the workspaces.generate API from your application.

While any application can generate a workspace object, and use that workspace object to restore the desktop, we recommend keeping that functionality in one application that will always be open (like an App Launcher or dedicated Layout Manager app).

//import the client module
import * as layouts from "openfin-layouts"

async function saveCurrentWorkspace {
   const workspaceObject = await layouts.workspaces.generate();
   //persist the workspaceObject somewhere of your choosing
   saveWorkspace(workspaceObject);
   return workspaceObject;
}

Adding custom state data to the workspace representation

Applications may register a function which will pass any custom state data to the layouts service whenever workspaces.generate is called. This state data will be passed back to the application (as customData property) during the restoration process kicked off by the workspaces.restore method.

//import the client module
import * as layouts from "openfin-layouts"

layouts.workspaces.setGenerateHandler(() => {
   //return custom data 
   return {"foo":"bar"};
});

Filtering windows

By default, all windows will be captured and included in the generated Workspace representation. There are different reasons for wanting to opt a window out of Layouts, temporary windows, menus, etc. However, you may have windows that you don’t want to restore, like temporary windows and menus. To opt a window out of Layouts, Deregister the windows using the deregister method. This will opt the window out of all Layouts functionality. It is the recommended pattern for handling windows serving a special UX function such as a floating menu or a modal dialogue.

Persisting workspaces

The service does not provide a storage solution for the generated JSON. Once the data is generated, it can be saved in the store of your choosing.

Restoring a workspace

When a workspace is restored, white placeholder boxes will appear to show that your application is loading. Do not close or move your desktop until the app is fully restored.

The workspaces.restore method takes a Workspace data structure and will automatically restore the Application and main Window states including sizing, positioning, and grouping/docking.

//import the client module
import * as layouts from "openfin-layouts"

layouts.workspaces.restore(workspaceObject).then(result => {
   //promise resolves with result once the layout has been restored
   handleResult(result)
})

Child windows

While the Layouts Service can automate the collection of child window state and the launching of an application’s main window, the relaunching of child windows has to be managed at the individual application level. For example, a typical chat application would need the user to be authenticated before child windows can be opened with content. The service is unaware of these situations, so we leave child window restoration up to the application.

The Workspace data object will provide the application with a WorkspaceApp structure containing everything it needs to restore its child windows to their previous state. The application simply assigns a handler function using the workspaces.setRestoreHandler method which handles the logic of window restoration. Please find an example function to position your windows here.

//import the client module
import * as layouts from "openfin-layouts"

async function appRestoreHandler(workspaceApp) {
    const ofApp = await fin.Application.getCurrent();
    const openWindows = await ofApp.getChildWindows();
    //iterate through the child windows of the workspaceApp data 
    const opened = workspaceApp.childWindows.map(async (win, index) => {
        //check for existence of the window
        if (!openWindows.some(w => w.identity.name === win.name)) {
            //create the OpenFin window with the same name
            const ofWin = await openChild(win.name, win.info.url);
            //position the window based on the data in the workspaceApp 
            await positionWindow(win);
        } else {
            //only position if the window exists
            await positionWindow(win);
        }
    });

    //wait for all windows to open and be positioned before returning
    await Promise.all(opened);
    return layoutApp;
}

layouts.workspaces.setRestoreHandler(appRestoreHandler);

Custom state data

As described in layout representation custom state data section above, applications can save arbitrary state data with a layout to be used as it pleases on restoration. For example, in this case, the application will use it’s saved customData to pass additional state to child windows after their creation.

async function appRestore(layoutApp) {
    const ofApp = await fin.Application.getCurrent();
    const openWindows = await ofApp.getChildWindows();
    //iterate through the child windows of the layoutApp data 
    const opened = layoutApp.childWindows.map(async (win, index) => {
        //check for existence of the window
        if (!openWindows.some(w => w.identity.name === win.name)) {
            //create the window if needed
            const ofWin = await openChild(win.name, win.info.url);
            //position the window based on the data in the workspaceApp
            await positionWindow(win);
        } else {
            //only position if the window exists
            await positionWindow(win);
        }
        if (win) {
            //check for customData for the window
            if (layoutApp.customData && layoutApp.customData[win.name]) {
                //use some function to pass the data to the window
                await sendData(win, layoutApp.customData[win.name]);
            }
        }
    });

    //wait for all windows to open and be positioned before returning
    await Promise.all(openAndPosition);
    return layoutApp;
}

Tabbing

  • Acts only on OpenFin applications that have included layouts in the application config
  • Dropping a window on top of another window will create tabbed windows with a tabstrip on top
  • Tabs can be reorder and renamed
  • Minimize / maximize / restore / close on the tabstrip affects the whole tabgroup (tabstrip and tabbed windows)
  • Application developers can provide their own tabstrip simply by hosting a templated html and enabling to it either programmatically or in the app config
  • The service comes with a win10 like default tabstrip that will used if no custom tabstrip is defined

Using a custom tab UI (tabstrip)

The Layouts service comes with a default UI tabstrip that will be used when tabbing two or more windows together. To use a custom tabstrip in your application you can use app config settings or use the tabbing.setTabstrip API:

//import the client module
import * as layouts from "openfin-layouts"

layouts.tabbing.setTabstrip({url: "http://localhost:8080/tabstrip.html", height: 50});

Building your own tabstrip

We suggest basing your custom tabstrip on the example layouts-tabstrip repository. The default tabstrip in the service is based on this repository and it gives basic tabbing, UI elements, and shows how to wire the tabbing events:

  • tab-added
  • tab-removed
  • tab-activated
  • tab-properties-updated

Repository overview

  • Images in /css/image
  • CSS in /css
  • Typescript code
    • Separate tabs on the tabstrip – /src/TabItem.ts
    • Tabstrip – /src/TabManager.ts
    • Wiring and setup – src/main.ts
  • HTML, with template section – /res/tabstrip.html

Deploying a custom tabstrip

The custom tabstrip with html with code/css/images need to be hosted on a http(s) server reachable by the end-user, and referenced by the absolute URL when enabled using setTabstrip.

Deregistering windows

You can programmatically register or deregister windows from the service using the client-side API. Certain windows such as a floating menu or a modal dialogue likely should not participate in layouts functionality. In these cases, windows can be opted out by using the deregister API. When deregister is called, the affected window will not snap, form tab groups, or have its state saved in a layout. Windows can be re-registered with the register API.

//import the client module
import * as layouts from "openfin-layouts"

layouts.deregister();

Deregister a different window

layouts.deregister({"uuid":"services-demo",name:"deregisterWin"});

Deregister multiple windows using regular expressions

deregister({uuid: "my-uuid", name: {expression: "popup-.*"}});

Have any questions? Get in touch with us at support@openfin.co.

Layouts API


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.