Poor man's (or smart man's?) docking framework!
MultiSplitPane: Splitting Without Nesting
Long ago, when Xerox defined the leading edge of the desktop GUI, applications flooded the desktop with top-level windows: main windows, alternative views, palettes, inspectors, dialog boxes spewing dialog boxes, editors, and on and on. A whole multitude of overlapping, titled, monochrome rectangles arranged in a way that mimicked a very messy, real-world desktop. Over time, our desire to simulate a messy pile of papers has waned. Modern applications tend to limit the use of top-level windows to alert, configuration, and other ephemeral tasks that seem to warrant a brief slice of the user's undivided attention. Everything else is packed into a tiled main window that's managed by the application and can be reconfigured by the user. GUI frameworks for building reconfigurable tiled main windows are usually called "docking frameworks." Docking frameworks make it possible to mimic a preternaturally neat desktop. Among the many organizational features that most docking frameworks provide is support for interactively resizing tiles by mouse-dragging in the gaps that separate the tiles.
MultiSplitPane is not a general purpose docking framework. It's a Swing container that just supports a resizable tiled layout of arbitrary components. It's intended to be a generalization of the existing Swing JSplitPane component, which only supports a pair of tiles. The MultiSplitLayout layout manager recursively arranges its components in row and column groups called "splits." Elements of the layout are separated by gaps called "dividers" that can be moved by the user, in the same way as JSplitPane. The overall layout is defined with a simple tree-structured model that can be stored and retrieved to make the user's layout configuration persistent. The initial layout, before the user has intervened, is defined conventionally, in terms of the layout model and the component's preferred sizes.
MultiSplitPane differs from components with similar capabilities in that complex dynamic layouts can be defined without nesting or composition. All of the children managed by a MultiSplitPane are arranged in their rows and columns (and rows within columns and columns within rows) end up separated by divider gaps, but not by extra layout-managing containers. MultiSplitPane's layout class, MultiSplitLayout, is also a little unusual in that it exposes a model of the complete layout. Most layout managers have a complex internal model that represents the layout, and some, like GridBagLayout, even support ad-hoc access to the model. MultiSplitPane provides explicit access to the complete layout model, in the same way that Swing components provide access to their data models. The motivation for this wasn't just flexibility, or separation of concerns. A single explicit layout model means that a more elaborate layout management system, like a docking framework, can be layered on top of MultiSplitPane without requiring burdensome assumptions about the type or structure of the component hierarchy. Having a separable model also means that the layout can be archived and restored by writing and reading (just) the model. The final section in this article, "Making MultiSplitPane Layouts Persistent," describes how to do this, using the java.beans (XMLEncoder and XMLDecoder) persistence API.
Basic MultiSplitPane Usage
Using MultiSplitPane requires two steps. First, a tree model that specifies the layout is created using the MultiSplitLayout's Split, Divider, and Leaf classes. These classes are static inner classes of MultiSplitLayout, so they have names like MultiSplitLayout.Divider (design note: this seemed preferable to MultiSplitLayoutDividerNode; by importing the static inner classes, we can refer to them by the unqualified names, like Divider and Split). Leaf nodes represent components, dividers represent the gaps between components that the user can drag around, and splits represent rows or columns. Components are added to the MultiSplitPane with a constraint that names the Leaf (leaf nodes have a name property) that will specify their bounds.
Here's an example that creates the MultiSplitPane equivalent of JSplitPane. There are just two components, arranged in a row, with a Divider in between.
Code: Select all
List children = Arrays.asList(new Leaf("left"), new Divider(), new Leaf("right")); Split modelRoot = new Split(); modelRoot.setChildren(children); MultiSplitPane multiSplitPane = new MultiSplitPane(); multiSplitPane.getMultiSplitLayout().setModel(modelRoot); multiSplitPane.add(new JButton("Left Component"), "left"); multiSplitPane.add(new JButton("Right Component"), "right");
. (see rest at source)
This article has focused on MultiSplitPane basics: how to use it, how the layout algorithm works, and how to save and restore layouts using the standard java.beans persistence API. MultiSplitPane does have other features and capabilities that you can learn about by surveying the Javadoc. For example:
1) It supports the continuousLayout property, as in JSplitPane, which defers layout until after the user has finished dragging a divider (or hits Esc to cancel the gesture).
2) Dynamic changes to the layout model are possible. For example, one could add/remove a Leaf or an entire Split, or make a coordinated change to a set of Dividers. Calling MultiSplitPane.revalidate() causes the managed components to be synched with the model.
3) MultiSplitPane is accessible; it overrides getAccessibleContext() to provide a custom AccessibleJComponent.
- MultiSplitPane source: MultiSplit.zip