A lightweight, high-performance, and fully customizable docking layout system for Java and Kotlin desktop applications built on top of FlatLaf.
This framework allows developers to build flexible IDE-like user interfaces with collapsible sidebars, tear-out floating utility windows, and robust layout persistence.
- Tri-Sidebar Anchor Topology: Seamlessly dock panels to the
WEST,EAST, orSOUTHregions of your application. - Floating Windows (Detachment): Context-click any dock to detach it into an autonomous floating
JDialogutility window that remembers its dimensions and coordinates. - Dynamic Layout: Advanced routing of the central canvas component using
COMPRESSEDorEXTENDEDhorizontal bounding modes for the southern dock layout. - State Persistence: Capture, serialize, and fully restore complex workspace topologies, divider locations, and
floating window bounds using standard JVM
.propertiesfiles. - FlatLaf Native Look & Feel: Built from the ground up utilizing
FlatSplitPane,FlatToolBar, andFlatToggleButtonfor a modern, sleek interface.
The framework leverages a fluent WorkspaceBuilder pattern to construct and assemble the structural topography of your
user interface.
val centralCanvas = JPanel()
// Assemble the workspace topology using the Builder
val workspace = Workspace.WorkspaceBuilder()
.southLayoutMode(Workspace.SouthLayoutMode.EXTENDED)
.setCentralComponent(centralCanvas)
// Register a factory to resolve components on-demand (essential for state restoration)
.setSingleDockFactory { id ->
when (id) {
"project-view" -> Pair(ProjectViewDock(), SwingConstants.WEST)
"console-log" -> Pair(ConsoleDock(), SwingConstants.SOUTH)
else -> null
}
}
// Pre-dock default components at startup
.dock("project-view", SwingConstants.WEST)
.dock("console-log", SwingConstants.SOUTH)
.build()
// Mount onto a standard JFrame
JFrame("My App IDE").apply {
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
contentPane = workspace
setSize(1024, 768)
setLocationRelativeTo(null)
isVisible = true
}The framework offers two structural layout hierarchy options via .southLayoutMode(...).
You can visualize how the workspace area is divided into geometric rectangular regions below:
-
COMPRESSEDmode: The southern panel is constrained strictly in the middle, sitting rectangularly between theWESTandEASTsidebar columns.+--------------------------------------------------------+ | North Toolbars | +--------------------------------------------------------+ | | | | | | Central Component | | | | | | | WEST |----------------------------------| EAST | | Sidebar | | Sidebar | | | SOUTH Panel | | | | (Compressed) | | | | | | +--------------------------------------------------------+ -
EXTENDEDmode: The southern panel spans across the entire horizontal footprint of the window, sitting completely underneath both theWESTandEASTcolumns.+--------------------------------------------------------+ | North Toolbars | +--------------------------------------------------------+ | | | | | WEST | Central Component | EAST | | Sidebar | | Sidebar | | | | | +--------------------------------------------------------+ | SOUTH Panel | | (Extended) | +--------------------------------------------------------+
The look and feel of the docking framework is fully decoupled from the core logic and can be stylized globally using
UIManager look-and-feel tokens.
By targeting the custom "kdock.*" property keys before building your UI, you can fully match your application's active
FlatLaf dark or light theme.
| Key Property | Type | Description |
|---|---|---|
kdock.header.background |
Color |
The background color of the title bar (AbstractDockHeader) for each docked panel. |
kdock.header.foreground |
Color |
The foreground (text) color of the dock title. |
kdock.header.font |
Font |
The typography font applied to the title text label inside the header. |
kdock.divider.size |
Int |
The divider size of the component splitter (FlatSplitPane). |
kdock.icon.minimize |
Icon |
The icon of the header button which minimizes the dock. |
kdock.icon.close |
Icon |
The icon of the header button which closes the dock. |
Saving and loading the user's customized sidebar state layout requires just a simple method invoke target:
val configFile = File("user-layout.properties")
// Save current topology state layout configuration
workspace.saveLayoutConfiguration(configFile)
// Load and restore layout configuration on next startup
workspace.loadLayoutConfiguration(configFile)By default, Workspace serializes the layout topology into a flat standard .properties
file through an internal implementation.
However, the architecture is fully decoupled from any specific storage format.
If you prefer to store the layout configuration using other formats (such as JSON, XML, or INI)
you can implement your own custom serialization logic by implementing the LayoutPersister interface
and registering it into the PersistenceManager.
-
Implement the Interface Create a class that implements
LayoutPersisterand handle the serialization/deserialization logic using your library of choice (e.g., Jackson, Gson, or kotlinx.serialization for JSON):class JsonLayoutPersister : LayoutPersister { override fun save(state: WorkspaceState, output: File) { // Your custom logic to convert WorkspaceState to JSON string // and write it down to the output file val jsonString = customJsonSerializer.encodeToString(state) output.writeText(jsonString) } override fun load(output: File): WorkspaceState? { // Your custom logic to read the file and map it back // to a valid WorkspaceState instance topology tree if (!output.exists()) return null return try { customJsonSerializer.decodeFromString<WorkspaceState>(output.readText()) } catch (e: Exception) { null } } }
-
Register Your Custom Engine Before triggering any save or load operation (typically during your application bootstrap sequence), swap the default engine in the global configuration instance:
// Register your custom persistence layout strategy
PersistenceManager.layoutPersister = JsonLayoutPersister()Once registered, all subsequent invocations of workspace.saveLayoutConfiguration(file) and
workspace.loadLayoutConfiguration(file) will seamlessly delegate processing to your custom engine
behind the scenes.