LayoutLib — Layout Container Library

Invisible container controls for organizing and grouping other controls in Plan9Basic programs. Layouts have no visual background, making them ideal for structuring your user interface without affecting appearance. Features include alignment-based responsive layouts, margins, padding, clipping, child management, locking, a custom OnPaint event for owner-drawing, and a full mouse event system. 85 functions.

CategoryCountDescription
Error Handling4layout_error, errormsg$, strerror$, clearerror
Creation & Destruction4layout# (3 overloads), layout_free
Position & Size14x, y, width, height (get/set), bounds#, move#, size#
Alignment & Margins12align (get/set), margin#, margins#, marginleft/top/right/bottom (get/set)
Padding10paddingleft/top/right/bottom (get/set), padding#, paddings#
Visibility & State12visible, enabled, opacity, hittest, clipchildren, locked (get/set)
Tag & Parent6tag (get/set), parent# (get/set), bringtofront#, sendtoback#
Children2childcount, child#
Utility1invalidate#
Events209 event types × set/get + clearcallbacks#

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
Linux✅ Full SupportGTK-based
Android✅ Full SupportMobile-optimized

Error Handling

FunctionSignatureDescription
layout_error()layout_error@Last error code (0 = no error)
layout_errormsg$()layout_errormsg$@Last error message as string
layout_strerror$(code)layout_strerror$@nDescription for a given error code
layout_clearerror()layout_clearerror@Clear the error state

Numeric Values Reference

Control Alignment layout_align#

ValueDescriptionUse Case
0None (absolute positioning)Manual x, y placement
1TopToolbars, headers
2LeftSidebars, navigation menus
3RightSide panels, property areas
4BottomFooters, status areas
9Client (fill parent)Main content area
11CenterCentered content regions

Creation & Destruction

FunctionSignatureDescription
layout#(parent#)layout#@#Create layout with default size (100×100)
layout#(parent#, w, h)layout#@#nnCreate with specified size (at 0, 0)
layout#(parent#, x, y, w, h)layout#@#nnnnCreate with position and size
layout_free(lay#)layout_free@#Destroy layout and all its child controls
╯ plan9basic
' Toolbar layout docked to top
let toolbar# = layout#(frm#, 600, 40)
layout_align#(toolbar#, 1)

' Content layout filling remaining space
let content# = layout#(frm#)
layout_align#(content#, 9)

' Positioned layout
let panel# = layout#(frm#, 20, 20, 560, 360)
⚠ Warning: layout_free destroys the layout and all its child controls. Do not reference any children after freeing their parent layout.

Position & Size

FunctionSignatureDescription
layout_x(lay#)layout_x@#Get X position
layout_x#(lay#, x)layout_x#@#nSet X position
layout_y(lay#)layout_y@#Get Y position
layout_y#(lay#, y)layout_y#@#nSet Y position
layout_width(lay#)layout_width@#Get width
layout_width#(lay#, w)layout_width#@#nSet width
layout_height(lay#)layout_height@#Get height
layout_height#(lay#, h)layout_height#@#nSet height
layout_bounds#(lay#, x, y, w, h)layout_bounds#@#nnnnSet position and size in one call
layout_move#(lay#, x, y)layout_move#@#nnSet position only
layout_size#(lay#, w, h)layout_size#@#nnSet size only

Alignment & Margins

When alignment is set, the layout is automatically positioned and sized by its parent. Margins control spacing between the layout and its siblings.

FunctionSignatureDescription
layout_align(lay#)layout_align@#Get control alignment
layout_align#(lay#, n)layout_align#@#nSet alignment (0=none, 1=top, 2=left, 3=right, 4=bottom, 9=client, 11=center)
layout_margin#(lay#, n)layout_margin#@#nSet uniform margin on all four sides
layout_margins#(lay#, l, t, r, b)layout_margins#@#nnnnSet individual margins (left, top, right, bottom)
layout_marginleft(lay#)layout_marginleft@#Get left margin
layout_marginleft#(lay#, n)layout_marginleft#@#nSet left margin
layout_margintop(lay#)layout_margintop@#Get top margin
layout_margintop#(lay#, n)layout_margintop#@#nSet top margin
layout_marginright(lay#)layout_marginright@#Get right margin
layout_marginright#(lay#, n)layout_marginright#@#nSet right margin
layout_marginbottom(lay#)layout_marginbottom@#Get bottom margin
layout_marginbottom#(lay#, n)layout_marginbottom#@#nSet bottom margin
╯ plan9basic
' Standard 3-area layout
let toolbar# = layout#(frm#, 600, 40)
layout_align#(toolbar#, 1)         ' Top

let sidebar# = layout#(frm#, 200, 400)
layout_align#(sidebar#, 2)         ' Left
layout_margin#(sidebar#, 2)        ' 2px gap all sides

let content# = layout#(frm#)
layout_align#(content#, 9)         ' Client (fill remaining)

Padding

Padding controls the space between the layout's edges and its child controls. Unlike margins (which affect spacing outside), padding affects spacing inside.

FunctionSignatureDescription
layout_padding#(lay#, n)layout_padding#@#nSet uniform padding on all four sides
layout_paddings#(lay#, l, t, r, b)layout_paddings#@#nnnnSet individual padding (left, top, right, bottom)
layout_paddingleft(lay#)layout_paddingleft@#Get left padding
layout_paddingleft#(lay#, n)layout_paddingleft#@#nSet left padding
layout_paddingtop(lay#)layout_paddingtop@#Get top padding
layout_paddingtop#(lay#, n)layout_paddingtop#@#nSet top padding
layout_paddingright(lay#)layout_paddingright@#Get right padding
layout_paddingright#(lay#, n)layout_paddingright#@#nSet right padding
layout_paddingbottom(lay#)layout_paddingbottom@#Get bottom padding
layout_paddingbottom#(lay#, n)layout_paddingbottom#@#nSet bottom padding
╯ plan9basic
' 10px padding on all sides
layout_padding#(lay#, 10)

' Different padding per side
layout_paddings#(lay#, 15, 10, 15, 10)
ⓘ Note: Margins = space outside the layout (between layout and its siblings). Padding = space inside the layout (between edge and its children). Both work together for well-spaced UIs.

Visibility & State

FunctionSignatureDescription
layout_visible(lay#)layout_visible@#Get visibility (0/1)
layout_visible#(lay#, n)layout_visible#@#nSet visibility (hides layout and all children)
layout_enabled(lay#)layout_enabled@#Get enabled state (0/1)
layout_enabled#(lay#, n)layout_enabled#@#nSet enabled state (disables layout and all children)
layout_opacity(lay#)layout_opacity@#Get opacity (0.0–1.0)
layout_opacity#(lay#, value)layout_opacity#@#nSet opacity (affects children too)
layout_hittest(lay#)layout_hittest@#Get hit-test state (0/1)
layout_hittest#(lay#, n)layout_hittest#@#nEnable/disable mouse hit testing
layout_clipchildren(lay#)layout_clipchildren@#Get clip-children mode (0/1)
layout_clipchildren#(lay#, n)layout_clipchildren#@#nEnable/disable clipping of child controls to layout bounds
layout_locked(lay#)layout_locked@#Get locked state (0/1)
layout_locked#(lay#, n)layout_locked#@#nLock/unlock layout to prevent user interaction
╯ plan9basic
' Enable clipping so children don't draw outside
layout_clipchildren#(lay#, 1)

' Make layout click-through
layout_hittest#(lay#, 0)

' Lock to prevent interaction
layout_locked#(lay#, 1)
ⓘ Note: layout_clipchildren# clips child controls to the layout's rectangular bounds. Enable this when children might extend beyond the layout (e.g., large labels, animations, or scroll-like behavior).

Tag & Parent

FunctionSignatureDescription
layout_tag(lay#)layout_tag@#Get user-defined integer tag
layout_tag#(lay#, n)layout_tag#@#nSet user-defined integer tag
layout_parent#(lay#)layout_parent#@#Get parent control pointer
layout_parent#(lay#, parent#)layout_parent#@##Move layout to a different parent
layout_bringtofront#(lay#)layout_bringtofront#@#Bring to front of Z-order
layout_sendtoback#(lay#)layout_sendtoback#@#Send to back of Z-order

Children Management

FunctionSignatureDescription
layout_childcount(lay#)layout_childcount@#Get the number of child controls
layout_child#(lay#, index)layout_child#@#nGet child control pointer at index (0-based)
╯ plan9basic
' Iterate all children
let cnt = layout_childcount(lay#)
for i = 0 to cnt - 1
    let child# = layout_child#(lay#, i)
    println "Child " + str$(i)
next

Utility

FunctionSignatureDescription
layout_invalidate#(lay#)layout_invalidate#@#Force the layout and its children to redraw
╯ plan9basic
' Force redraw (useful after programmatic changes or with OnPaint)
layout_invalidate#(lay#)
ⓘ Note: layout_invalidate# is especially useful in combination with layout_onpaint# — call it whenever you need the OnPaint handler to execute again.

Events

Each event has a setter (layout_onXXX#(lay#, func$)) and a getter (layout_onXXX$(lay#)). Use layout_clearcallbacks#(lay#) to disconnect all callbacks at once.

Click Events

Event SetterGetterCallback Signature
layout_onclick#(lay#, func$)layout_onclick$(lay#)function name(sender#)
layout_ondblclick#(lay#, func$)layout_ondblclick$(lay#)function name(sender#)

Mouse Events

Event SetterGetterCallback Signature
layout_onmousedown#(lay#, func$)layout_onmousedown$(lay#)function name(sender#, button, x, y, shift$)
layout_onmouseup#(lay#, func$)layout_onmouseup$(lay#)function name(sender#, button, x, y, shift$)
layout_onmousemove#(lay#, func$)layout_onmousemove$(lay#)function name(sender#, x, y, shift$)
layout_onmouseenter#(lay#, func$)layout_onmouseenter$(lay#)function name(sender#)
layout_onmouseleave#(lay#, func$)layout_onmouseleave$(lay#)function name(sender#)
layout_onmousewheel#(lay#, func$)layout_onmousewheel$(lay#)function name(sender#, delta, shift$)
ⓘ Note: OnMouseWheel provides a delta value: positive for scroll up, negative for scroll down.

Other Events

Event SetterGetterCallback SignatureWhen It Fires
layout_onpaint#(lay#, func$)layout_onpaint$(lay#)function name(sender#)When the layout needs to repaint (custom drawing)
layout_onresize#(lay#, func$)layout_onresize$(lay#)function name(sender#)Layout is being resized (fires continuously)
layout_onresized#(lay#, func$)layout_onresized$(lay#)function name(sender#)Layout resize is complete (fires once)
layout_clearcallbacks#(lay#)Disconnect all event callbacks
ⓘ Note: OnPaint is unique to LayoutLib — it fires when the layout needs repainting, allowing custom owner-drawn content. Call layout_invalidate# to trigger a repaint. Not available in PanelLib.
ⓘ Note: OnResize fires continuously during resizing. OnResized fires once when resizing is complete. Use OnResized when you only need the final dimensions.

Layout vs Panel

FeatureLayoutPanel
VisibleNo (fully transparent)Yes (has background)
BackgroundNonePlatform default
OnPaint event✅ Yes (custom drawing)❌ No
Use CaseInvisible grouping / custom drawingVisible sections / cards
PerformanceLighter weightSlightly heavier
ClippingYes (layout_clipchildren#)Yes (panel_clipchildren#)
PaddingYesYes
Children accessYes (childcount / child#)Yes (childcount / child#)
Drag eventsNoYes

Use Layout when you need invisible organization or custom owner-drawn content. Use Panel when you need visible separation or styled containers.

Complete Examples

Toolbar Layout

╯ toolbar.bas
let frm# = form#("Toolbar Demo", 600, 400)
form_position#(frm#, 4)

' Toolbar at top
let toolbar# = layout#(frm#, 600, 40)
layout_align#(toolbar#, 1)

let btnNew# = button#(toolbar#, "New", 5, 5, 60, 30)
let btnOpen# = button#(toolbar#, "Open", 70, 5, 60, 30)
let btnSave# = button#(toolbar#, "Save", 135, 5, 60, 30)

' Content area
let content# = layout#(frm#)
layout_align#(content#, 9)

let lblContent# = label#(content#, "Content area")
label_move#(lblContent#, 250, 150)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Sidebar Navigation

╯ sidebar.bas
let frm# = form#("Sidebar Demo", 800, 500)
form_position#(frm#, 4)

' Left sidebar
let sidebar# = layout#(frm#, 200, 500)
layout_align#(sidebar#, 2)

let btn1# = button#(sidebar#, "Menu 1", 10, 20, 180, 35)
let btn2# = button#(sidebar#, "Menu 2", 10, 60, 180, 35)
let btn3# = button#(sidebar#, "Menu 3", 10, 100, 180, 35)

' Main content
let main# = layout#(frm#)
layout_align#(main#, 9)

let lblMain# = label#(main#, "Main Content Area")
label_move#(lblMain#, 200, 200)
label_fontsize#(lblMain#, 20)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Nested Layouts

╯ nested.bas
let frm# = form#("Nested Layouts", 600, 400)
form_position#(frm#, 4)

' Outer container
let outer# = layout#(frm#, 20, 20, 560, 360)

' Top section
let top# = layout#(outer#, 0, 0, 560, 100)
let lblTop# = label#(top#, "Header Section")
label_move#(lblTop#, 220, 40)
label_fontsize#(lblTop#, 18)

' Bottom section with two columns
let bottom# = layout#(outer#, 0, 110, 560, 250)

let leftCol# = layout#(bottom#, 0, 0, 275, 250)
let lblLeft# = label#(leftCol#, "Left Column")
label_move#(lblLeft#, 100, 100)

let rightCol# = layout#(bottom#, 285, 0, 275, 250)
let lblRight# = label#(rightCol#, "Right Column")
label_move#(lblRight#, 100, 100)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Clipping Demo

╯ clipping.bas
let frm# = form#("Clipping Demo", 400, 300)
form_position#(frm#, 4)

' Layout with clipping enabled
let lay# = layout#(frm#, 50, 50, 200, 150)
layout_clipchildren#(lay#, 1)

' Large label that extends beyond layout bounds
let lbl# = label#(lay#, "This is a very long text that will be clipped")
label_bounds#(lbl#, 10, 60, 300, 30)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Responsive Three-Section Layout

╯ responsive.bas
let frm# = form#("Responsive Layout", 800, 500)
form_position#(frm#, 4)

' Header - docked top
let header# = layout#(frm#, 800, 50)
layout_align#(header#, 1)
layout_padding#(header#, 10)
let lblTitle# = label#(header#, "My Application")
label_fontsize#(lblTitle#, 18)
label_bold#(lblTitle#, 1)

' Footer - docked bottom
let footer# = layout#(frm#, 800, 30)
layout_align#(footer#, 4)
layout_padding#(footer#, 5)
let lblStatus# = label#(footer#, "Ready")

' Sidebar - docked left
let sidebar# = layout#(frm#, 180, 420)
layout_align#(sidebar#, 2)
layout_margin#(sidebar#, 2)
let btn1# = button#(sidebar#, "Dashboard", 5, 10, 170, 35)
let btn2# = button#(sidebar#, "Settings", 5, 50, 170, 35)
let btn3# = button#(sidebar#, "About", 5, 90, 170, 35)

' Content - fills remaining space
let content# = layout#(frm#)
layout_align#(content#, 9)
layout_padding#(content#, 20)
let lblContent# = label#(content#, "Dashboard")
label_fontsize#(lblContent#, 24)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Best Practices

PracticeWhy
Use layout_align# for responsive layoutsControls resize automatically with their parent
Enable layout_clipchildren#Prevents children from drawing outside layout bounds
Nest layouts for complex structuresCombine alignment modes (top + left + client) for multi-section UIs
Use padding for content insetControls gap between layout edge and children
Use margins for spacing between layoutsControls gap between sibling layouts
Add aligned layouts in order: edges first, client lastTop/Bottom/Left/Right layouts claim space; Client fills what remains
Use Layout instead of Panel when no background is neededLayouts are lighter weight and fully transparent
Use layout_onpaint# for custom drawingUnique to Layout — enables owner-drawn content
Use layout_onresized# over layout_onresize#OnResized fires once when complete, avoiding per-pixel recalculations
⚠ Warning: When using alignment, add edge-aligned controls (Top, Bottom, Left, Right) before the Client-aligned control. The Client layout fills whatever space remains after edges have been allocated.

Quick Reference

FunctionSignatureDescription
layout_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
layout#(parent#[, w, h] | [, x, y, w, h])variousCreate (3 overloads)
layout_free(lay#)layout_free@#Destroy (and all children)
layout_x/y/width/height (get/set) / bounds# / move# / size#variousPosition & size (14)
layout_align / margin# / margins# / margin[left/top/right/bottom]variousAlignment & margins (12)
layout_padding# / paddings# / padding[left/top/right/bottom]variousPadding (10)
layout_visible / enabled / opacity / hittest / clipchildren / lockedvariousVisibility & state (12)
layout_tag / parent# / bringtofront# / sendtoback#variousTag & parent (6)
layout_childcount / child#variousChildren (2)
layout_invalidate#layout_invalidate#@#Force redraw (1)
layout_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onpaint/onresize/onresizedvariousEvents set+get (19)
layout_clearcallbacks#layout_clearcallbacks#@#Disconnect all events

85 functions. Part of the Plan9Basic GUI library system.