dockcontainer — u_pbt_dockcontainer #
← Component reference · Guide contents
Visual Studio style dockable panels: drag-and-drop docking, tab stacking, resizable splitters, floating windows, auto-hide — plus saving and restoring the layout.
▶ See it live — Demo application, Dock container tile: the preview, the code behind it and this page, side by side.
At a glance #
| Userobject | u_pbt_dockcontainer |
| Item class | n_pbt_dock_panel (one panel) |
| Used for | Giving a PowerBuilder window the ergonomics of a modern IDE: users arrange their own panels, and the layout is still there at the next session |
Just like tab, every panel hosts a real PowerBuilder control (dragobject): a userobject, a DataWindow, or another PBToolboxAI component. See Hosting real PowerBuilder controls.
Quick start #
// window open event : the panel controls are already placed on it
uo_dock.of_add_panel(/*key*/ "document", /*position*/ "", /*relative_to*/ "", &
/*size*/ 0, /*title*/ "Document", /*content*/ uo_editeur)
uo_dock.of_add_panel(/*key*/ "explorateur", /*position*/ uo_dock.POSITION_START, &
/*relative_to*/ "document", /*size*/ 260, &
/*title*/ "Explorer", /*content*/ uo_arbre)
uo_dock.of_add_panel(/*key*/ "proprietes", /*position*/ uo_dock.POSITION_END, &
/*relative_to*/ "document", /*size*/ 300, &
/*title*/ "Properties", /*content*/ uo_props)
// The central area : the docking cannot close, float or hide it
uo_dock.is_main = "document"
Properties #
| Property | Type | Default | Purpose |
|---|---|---|---|
is_main | string | "" | Key of the main panel: the central document area, which can neither be hidden nor floated. The panel must already exist |
is_theme_style | string | fluent | Visual style of the component (THEME_STYLE_* constants) |
is_theme_mode | string | light | Light or dark variant (THEME_MODE_* constants) |
il_theme_accent | long | -1 | Accent color of this component (-1 = the theme accent) |
is_tooltip | string | "" | Simple tooltip shown when hovering the component |
is_super_tooltip_title | string | "" | Title of the rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the rich tooltip |
Docking positions #
The component constants feed as_position in of_add_panel and of_move_panel:
| Constant | Value | Effect |
|---|---|---|
POSITION_START | "start" | The new panel docks on the reading start side of the reference panel (on the left in left-to-right writing) |
POSITION_END | "end" | … on the reading end side |
POSITION_TOP | "top" | … above it |
POSITION_BOTTOM | "bottom" | … below it |
POSITION_STACK | "stack" | The panel is stacked as a tab onto the reference panel. This is the value used when as_position is empty |
Properties of a panel — n_pbt_dock_panel #
Obtained through of_item(key).
| Property | Type | Default | Purpose |
|---|---|---|---|
is_title | string | "" | Title shown on the panel tab. Accepts rich text markup |
is_short_title | string | "" | Short form of the title, for the places where the room is scarce: the edge rail of a pinned panel (always — it is a sliver) and a stacked tab, but only once the full title no longer fits, which is measured rather than guessed. The panel header keeps the full title. "" = no short form |
ib_visible | boolean | true | Shows or hides this panel |
ib_pinned | boolean | true | true = pinned in place; false = the panel collapses to a strip on the edge and only slides out on hover (auto-hide) |
is_header_color | string | "" | Header colour of this panel. Empty = the theme decides; HEADER_COLOR_ACCENT follows the theme accent (and keeps following it after a theme change, which a fixed code would not); otherwise a #rrggbb. The readable text colour comes with it |
is_border_color | string | "" | Border colour of this panel, same grammar. It outlines the frame and the header, which each carry their own: colouring only one leaves a visible seam. It follows the panel into its fly-out |
is_tooltip | string | "" | Simple tooltip shown when hovering the item |
is_super_tooltip_title | string | "" | Title of the item rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the item rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the item rich tooltip |
Methods #
| Method | Purpose |
|---|---|
of_add_panel (string as_key, string as_position, string as_relative_to, integer ai_size, string as_title, dragobject ado_content) | Adds a closable panel and hosts the control. as_relative_to = key of the panel it docks against (empty = the root); ai_size = initial size in pixels (0 = automatic) |
of_add_panel (…, boolean ab_closable) | Same thing, with ab_closable = false for a panel without a close button |
of_select_panel (string as_key) | Brings a panel to the front of its tab group |
of_move_panel (string as_key, string as_target, string as_position) | Rearranges from code, exactly as if the user had dragged the tab |
of_move_panel (string as_key, string as_target, string as_position, integer ai_index) | Same thing, giving the panel's rank inside the target stack (0 = first, -1 = last). Only a stack is an ordered list: the index is ignored for POSITION_START / END / TOP / BOTTOM |
of_float_panel (string as_key) | Floats the panel into a real floating window that can be moved and resized. Closing it docks the panel back (ue_panel_docked) |
of_notify_panel (string as_key) | Signals new content: if the panel is not visible, its tab shows a count of unseen items, cleared when the panel is selected |
of_clear_notify (string as_key) | Clears that counter without showing the panel. Selecting the tab was the only other way, which is wrong when your code knows the content itself is gone |
of_remove_panel (string as_key) | Removes the panel and returns the control to its original window |
of_get_layout ( ) | Returns the current layout (split tree, ratios, tabs, visibility) as a string, ready to be stored in a file, a database or the registry |
of_set_layout (string as_json) | Restores a layout obtained from of_get_layout. The panels must have been re-created before the call; any panel missing from the layout is attached to the first group |
of_item (string as_id) | n_pbt_dock_panel handle of the panel (created on first access) |
of_refresh_panel (string as_key) | Refreshes the rendering of a panel built off-screen, without flicker |
of_relayout ( ) | Recomputes and republishes every area so that the hosted controls are repositioned |
of_reset ( ) | Empties the container: every hosted control is returned to its original window, the panel handles are released, and the stored layout is cleared |
of_set_redraw (boolean) | Groups a burst of changes into a single render |
of_save_as_png (string) · of_save_as_jpg (string) | Exports the rendering as an image |
Events #
| Event | Raised when |
|---|---|
ue_panel_selected (string as_key) | A panel becomes the active panel of its group |
ue_panel_closed (string as_key) | The user closes a panel |
ue_panel_pinned (string as_key, boolean ab_pinned) | A panel is pinned (true) or unpinned into auto-hide (false) |
ue_panel_floated (string as_key) | A panel is floated into a floating window |
ue_panel_docked (string as_key) | A floating panel is docked back |
ue_layout_changed (string as_json) | The layout has changed (drag, resize, docking). The parameter carries the new layout, ready to be saved |
ue_ready ( ) | The component has finished loading; everything sent beforehand has been replayed |
ue_runtime_missing ( ) | The WebView2 runtime is missing: the component stays empty |
ue_bg_color (long al_color) | The component has computed its theme background color; the userobject has already adopted it (backcolor) |
Keyboard #
A panel's content is a real PowerBuilder control: the keyboard there belongs to your code, as in any window. What the component takes care of is its own chrome — headers, tabs, splitters:
| Key | Effect |
|---|---|
| Arrows on a tab | Move to the previous / next panel of the group, and select it. A tab strip is a single tab stop |
| Home / End on a tab | First / last panel of the group |
| Arrows on a splitter | Move the separation by 2 %, along the splitter's axis only |
| Page Up / Page Down on a splitter | The same, in 10 % steps |
| Escape | Closes the sliding panel opened from an edge button |
A splitter is a focusable separator that announces its position; the header buttons (float, hide, close) carry a name, and the active panel of a group is marked aria-selected. A keyboard move is reported through ue_layout_changed exactly like a mouse drag.
Examples #
An IDE-style window #
// open event : every content is a real PB control placed on the window
uo_dock.of_add_panel("document", "", "", 0, "Document", uo_editeur)
uo_dock.of_add_panel("explorateur", uo_dock.POSITION_START, "document", 260, "Explorer", uo_arbre)
uo_dock.of_add_panel("proprietes", uo_dock.POSITION_END, "document", 300, "Properties", uo_props)
uo_dock.of_add_panel("sortie", uo_dock.POSITION_BOTTOM, "document", 200, "Output", uo_console)
// A second panel stacked as a tab onto "proprietes"
uo_dock.of_add_panel("aide", uo_dock.POSITION_STACK, "proprietes", 0, "Help", uo_aide)
uo_dock.is_main = "document"
Remembering and restoring the user's layout #
// window close event : save what the user has arranged
string ls_disposition
ls_disposition = uo_dock.of_get_layout()
of_enregistrer_preference("dock_principal", ls_disposition)
// open event : RE-CREATE the panels first, restore afterwards
string ls_disposition
of_creer_les_panneaux() // the of_add_panel calls above
ls_disposition = of_lire_preference("dock_principal")
if ls_disposition <> "" then uo_dock.of_set_layout(ls_disposition)
The order matters:
of_set_layoutcreates no panel, it only rearranges the ones that already exist.
Tracking changes continuously #
// ue_layout_changed event of uo_dock : (string as_json)
// Saved right away, without waiting for the window to close.
of_enregistrer_preference("dock_principal", as_json)
Driving a panel through its handle #
n_pbt_dock_panel lnv_panneau
lnv_panneau = uo_dock.of_item("proprietes")
lnv_panneau.is_title = "Properties (3 changed)"
lnv_panneau.ib_pinned = false // collapses to a strip on the edge
// Hide a panel reserved for administrators
uo_dock.of_item("audit").ib_visible = gb_administrateur
Drawing attention to a background panel #
// A background task wrote to the console : signal it without stealing the focus
uo_dock.of_notify_panel("sortie")
Rearranging from code #
// Move a panel as if the user had dragged its tab
uo_dock.of_move_panel(/*key*/ "sortie", /*target*/ "document", /*position*/ "bottom")
// Stack it as a tab onto another panel
uo_dock.of_move_panel(/*key*/ "aide", /*target*/ "proprietes", /*position*/ "stack")
Floating a panel onto a second monitor #
// Native floating window : the user can drop it on another monitor
uo_dock.of_float_panel("proprietes")
// ue_panel_docked event of uo_dock : (string as_key)
// The user closed the floating window : the panel is back.
of_journaliser("Panel docked back : " + as_key)
Closing a panel cleanly #
// ue_panel_closed event of uo_dock : (string as_key)
// Also remove the panel from the container to release the hosted control.
uo_dock.of_remove_panel(as_key)
Best practices #
- Always designate a main panel with
is_main: without a central area, users can hide everything and end up staring at an empty window. - Save the layout on
ue_layout_changedrather than only on close: an abnormal shutdown then loses nothing. of_set_layoutcomes after theof_add_panelcalls, never before.- Use
of_notify_panelfor background tasks: it is less intrusive than a dialog box and the information stays visible. - A hosted control is a native window: it draws above the web layer, so no visual effect of the component can appear over it.
- Call
of_reset()before any rebuild: without it, the previously hosted controls stay reparented into the emptied container and reusing a key fails. - For simple exclusive pages, with no docking and no floating, tab is lighter.