Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pbSlintDock

A full-featured docking (dock panel) library for Slint — C++, Rust, and Python

日本語 README はこちら / Japanese README

Slint has no built-in docking system. pbSlintDock fills that gap with a complete, IDE-grade docking experience: nested splits, tab groups, drag & drop re-docking with drop indicators, native floating windows, and layout persistence — all driven by a GUI-independent C++ layout engine and rendered by plain .slint components.

Docking in action

Real capture of the demo: tab merge → split → new column → splitter resize → tab reorder. Recorded frame-by-frame from the running app.

Default layout

Drag & drop with drop-zone overlay, 5-way indicator and drag ghost:

Drag & drop overlay

New to the library? Start with the Integration Guide — a step-by-step walkthrough of adding pbSlintDock to your own Slint application, followed by the API Reference.


Features

  • Arbitrary nested splits — horizontal / vertical splits of any depth, n-ary (a split can hold any number of children), automatically normalized (no degenerate single-child splits, same-direction splits merge).
  • Tab groups — every leaf is a tab group. Active-tab highlight, per-tab close buttons, hover states, CJK-aware tab widths with eliding.
  • Drag & drop re-docking
    • Drag a tab (or a whole group by its empty tab-bar area) anywhere.
    • 5-way drop indicator (center / left / right / top / bottom) centred on the hovered group, plus window-edge root zones.
    • Live drop preview rectangle and a drag ghost chip that follows the cursor — optionally as its own top-level window, so the ghost can leave the source window and travel across the desktop.
    • Drop on a tab bar to insert at an exact tab position.
  • Tab reordering — drag a tab along its own tab bar to reorder in place.
  • Floating windows
    • Drag a tab out of the window → it detaches into a real, frameless OS window (multi-monitor friendly, clamped to the monitor work area).
    • Floating windows are full dock hosts: they have their own tab bar and can hold several panels. A float holding a single panel shows only its title bar — the redundant one-tab tab bar is hidden (configurable).
    • Drag a floating window by its title bar back over the main window to re-dock it — with the same overlay/zone UX.
    • 8-direction resize grips, close button, automatic window title.
  • Splitter resizing — hover-highlighted splitters with min-size constraints propagated through the tree; ratios are preserved on resize.
  • Layout persistence — one-call JSON save/restore including floating windows. Panels are referenced by string ids, so layouts survive registration-order changes; unknown ids are dropped gracefully.
  • Programmatic API — dock / float / close / open / activate panels from C++, Rust, or Python at any time. All bindings use the same engine and JSON persistence format.
  • Theming — every color lives in the PbDockTheme global (dark theme by default).
  • Engine is GUI-independent — the layout tree, solver, hit-testing and serialization compile without Slint and are covered by unit tests (tests/test_layout.cpp). The demo has a built-in UI self-test (--auto) that drives the real UI with synthetic pointer events.

Why a C++ layout engine?

Slint is declarative: components cannot recurse, cannot be re-parented at runtime, and z must be a literal. A docking system needs none of those if you flip the design around:

┌───────────────┐   flat, absolutely-positioned view-models   ┌──────────────┐
│ C++ engine    │ ───────────────────────────────────────────▶│ .slint view  │
│ (layout tree, │    [GroupVm] [TabVm] [SplitterVm] [PanelVm] │ (for-loops,  │
│ solver, drag  │ ◀─────────────────────────────────────────── │  TouchAreas) │
│ state machine)│        raw input events (press/move/…)      └──────────────┘
└───────────────┘

The C++ side owns the split tree and computes flat rectangles for every group, tab, splitter and panel. The .slint side renders those rectangles with for loops and reports raw pointer input back. Because geometry flows through reactive Slint models, everything re-layouts live — and because your panels are declared once and only moved by geometry, panel state (scroll positions, text, widgets) survives every dock operation inside a window.

Language support

Language Package / entry point UI integration
C++ pbslintdock::core, SlintBridge Full native bridge and demo
Rust bindings/rust crate Safe engine API and full Slint demo including float windows and drag ghost
Python bindings/python wheel DockManager + SlintBridge, including native float-window support on Windows

See the Rust and Python guide for build, packaging, API mapping, and complete examples.

Requirements

  • Slint 1.17+ C++ SDK (find_package(Slint))
  • CMake 3.21+, a C++20 compiler (verified with MSVC 2022 on Windows 11)
  • Rust 1.85+ for the Rust crate
  • Python 3.12+ and slint==1.17.1b2 for the Python package
  • Windows is the primary target (global cursor tracking and work-area clamping use Win32; both have graceful fallbacks on other platforms)

Getting the Slint SDK

third_party/ is not committed. Fetch the official binary SDK once:

  1. Download Slint-cpp-<version>-win64-MSVC-AMD64.exe from the Slint releases page.
  2. Extract/install it to third_party/slint (silent install works: Slint-cpp-….exe /S /D=<repo>\third_party\slint).

Any other install location works too — pass it via CMAKE_PREFIX_PATH.

Building the demo

cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
build\bin\Release\pbslintdock_demo.exe

Demo flags:

flag effect
--shot <file.bmp> render, save a screenshot of the main window, quit
--auto run the built-in UI self-test (synthetic pointer events), write %TEMP%\autotest_result.txt, exit code 0 on success
--record <dir> play the scripted docking showcase and write one BMP frame per tick into <dir> (this is how docs/docking.gif was made)

Engine unit tests: build\Release\pbslintdock_tests.exe.

Integrating into your app

1. CMake

add_subdirectory(pbSlintDock)          # provides pbslintdock::core
add_executable(my_app WIN32 main.cpp)
slint_target_sources(my_app app.slint
    LIBRARY_PATHS pbslintdock=${PBSLINTDOCK_UI_DIR}/pbslintdock.slint)
target_link_libraries(my_app PRIVATE pbslintdock::core Slint::Slint)

2. Your .slint

import { DockHost, DockPanel, FloatWindowFrame, DragGhostWindow,
         PbDock, PbDockTheme, PanelVm, TabVm, GroupVm, SplitterVm }
    from "@pbslintdock";

// re-export so the generated C++ header exposes the bridge types
export { PbDock, PbDockTheme, PanelVm, TabVm, GroupVm, SplitterVm,
         DragGhostWindow }

// declare your panels once; the library drives their geometry
component PanelSet inherits Rectangle {
    background: transparent;
    DockPanel { panel-id: "explorer"; MyExplorerView { } }
    DockPanel { panel-id: "console";  MyConsoleView { } }
}

export component MainWindow inherits Window {
    DockHost { PanelSet { } }
}

// floating windows embed the same panel set
export component MyFloatWindow inherits FloatWindowFrame {
    PanelSet { }
}

3. Your C++

#include "app.h"                        // slint-generated
#include "pbslintdock/slint_bridge.h"   // include AFTER the generated header

int main() {
    pbdock::DockManager dock;
    dock.add_panel({ "explorer", "Explorer" });
    dock.add_panel({ "console", "Console" });

    dock.dock_panel_root("explorer", pbdock::DropZone::RootLeft, 0.25f);
    dock.dock_panel("console", "explorer", pbdock::DropZone::Bottom);

    pbdock::SlintBridge bridge(dock);
    bridge.set_float_factory<MyFloatWindow>();   // before attach_main
    bridge.set_ghost_window<DragGhostWindow>();  // before attach_main
    auto app = MainWindow::create();
    bridge.attach_main(app);
    app->run();
}

That is the whole integration: everything else (drag & drop, floating windows, overlays, splitters) is handled by the library.

set_ghost_window<DragGhostWindow>() is optional: it promotes the drag ghost to its own top-level window so it can follow the cursor outside the source window. Omit it and the ghost is drawn inside the DockHost as before, clipped at the window edge.

For the long form — required call order, initial layout recipes, float factories, state sharing across windows, and troubleshooting — see the Integration Guide.

C++ API overview (pbdock::DockManager)

call effect
add_panel({id, title, closable, min_w, min_h}) register a panel (starts closed)
dock_panel(id, target_id, DropZone, ratio) dock relative to another panel
dock_panel_root(id, DropZone::Root*, ratio) dock at a window edge
float_panel(id, x, y, w, h) detach into a floating window
close_panel(id) / open_panel(id) / is_open(id) visibility control (reopen remembers the last neighbour)
activate_panel(id) select the panel's tab
save_layout()std::string serialize everything (JSON)
restore_layout(json) restore, recreating floating windows
metrics() tune tab bar height, splitter thickness and grab padding, tab widths, …

DropZone: Center, Left, Right, Top, Bottom, RootLeft, RootRight, RootTop, RootBottom, TabBar.

Theming

Override any color/metric of the PbDockTheme global from .slint or C++:

app->global<PbDockTheme>().set_accent(slint::Color::from_rgb_uint8(0xff, 0x99, 0x00));

State & floating windows — one caveat

Slint cannot move a live component between windows. When a panel floats (or re-docks from a float), its content component in the target window is a fresh instance. Keep the state you care about in Slint globals and mirror it in C++ — the demo (demo/main.cpp, DemoState) shows a compact pattern that syncs state across every window, so text typed into a panel survives float/dock round-trips.

Repository layout

include/pbslintdock/   public headers (engine + header-only Slint bridge)
src/                   engine implementation (no Slint dependency)
ui/pbslintdock.slint   the view layer (import as @pbslintdock)
demo/                  demo application (also the UI self-test)
tests/                 engine unit tests
docs/                  integration guide, API reference, screenshots

Documentation

Document Contents
Integration Guide (日本語) step-by-step setup, from CMake to a complete working example
API Reference (日本語) every public type and method, the PbDockTheme / Metrics field lists, the layout JSON schema

License

MIT — see LICENSE.

About

A full-featured docking (dock panel) library for Slint - C++

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages