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.
| Category | Count | Description |
|---|---|---|
| Error Handling | 4 | layout_error, errormsg$, strerror$, clearerror |
| Creation & Destruction | 4 | layout# (3 overloads), layout_free |
| Position & Size | 14 | x, y, width, height (get/set), bounds#, move#, size# |
| Alignment & Margins | 12 | align (get/set), margin#, margins#, marginleft/top/right/bottom (get/set) |
| Padding | 10 | paddingleft/top/right/bottom (get/set), padding#, paddings# |
| Visibility & State | 12 | visible, enabled, opacity, hittest, clipchildren, locked (get/set) |
| Tag & Parent | 6 | tag (get/set), parent# (get/set), bringtofront#, sendtoback# |
| Children | 2 | childcount, child# |
| Utility | 1 | invalidate# |
| Events | 20 | 9 event types × set/get + clearcallbacks# |
Cross-Platform Support
| Platform | Status | Notes |
|---|---|---|
| Windows | ✅ Full Support | Win32/Win64 |
| Linux | ✅ Full Support | GTK-based |
| Android | ✅ Full Support | Mobile-optimized |
Error Handling
| Function | Signature | Description |
|---|---|---|
layout_error() | layout_error@ | Last error code (0 = no error) |
layout_errormsg$() | layout_errormsg$@ | Last error message as string |
layout_strerror$(code) | layout_strerror$@n | Description for a given error code |
layout_clearerror() | layout_clearerror@ | Clear the error state |
Numeric Values Reference
Control Alignment layout_align#
| Value | Description | Use Case |
|---|---|---|
| 0 | None (absolute positioning) | Manual x, y placement |
| 1 | Top | Toolbars, headers |
| 2 | Left | Sidebars, navigation menus |
| 3 | Right | Side panels, property areas |
| 4 | Bottom | Footers, status areas |
| 9 | Client (fill parent) | Main content area |
| 11 | Center | Centered content regions |
Creation & Destruction
| Function | Signature | Description |
|---|---|---|
layout#(parent#) | layout#@# | Create layout with default size (100×100) |
layout#(parent#, w, h) | layout#@#nn | Create with specified size (at 0, 0) |
layout#(parent#, x, y, w, h) | layout#@#nnnn | Create with position and size |
layout_free(lay#) | layout_free@# | Destroy layout and all its child controls |
' 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)
layout_free destroys the layout and all its child controls. Do not reference any children after freeing their parent layout.Position & Size
| Function | Signature | Description |
|---|---|---|
layout_x(lay#) | layout_x@# | Get X position |
layout_x#(lay#, x) | layout_x#@#n | Set X position |
layout_y(lay#) | layout_y@# | Get Y position |
layout_y#(lay#, y) | layout_y#@#n | Set Y position |
layout_width(lay#) | layout_width@# | Get width |
layout_width#(lay#, w) | layout_width#@#n | Set width |
layout_height(lay#) | layout_height@# | Get height |
layout_height#(lay#, h) | layout_height#@#n | Set height |
layout_bounds#(lay#, x, y, w, h) | layout_bounds#@#nnnn | Set position and size in one call |
layout_move#(lay#, x, y) | layout_move#@#nn | Set position only |
layout_size#(lay#, w, h) | layout_size#@#nn | Set 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.
| Function | Signature | Description |
|---|---|---|
layout_align(lay#) | layout_align@# | Get control alignment |
layout_align#(lay#, n) | layout_align#@#n | Set alignment (0=none, 1=top, 2=left, 3=right, 4=bottom, 9=client, 11=center) |
layout_margin#(lay#, n) | layout_margin#@#n | Set uniform margin on all four sides |
layout_margins#(lay#, l, t, r, b) | layout_margins#@#nnnn | Set individual margins (left, top, right, bottom) |
layout_marginleft(lay#) | layout_marginleft@# | Get left margin |
layout_marginleft#(lay#, n) | layout_marginleft#@#n | Set left margin |
layout_margintop(lay#) | layout_margintop@# | Get top margin |
layout_margintop#(lay#, n) | layout_margintop#@#n | Set top margin |
layout_marginright(lay#) | layout_marginright@# | Get right margin |
layout_marginright#(lay#, n) | layout_marginright#@#n | Set right margin |
layout_marginbottom(lay#) | layout_marginbottom@# | Get bottom margin |
layout_marginbottom#(lay#, n) | layout_marginbottom#@#n | Set bottom margin |
' 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.
| Function | Signature | Description |
|---|---|---|
layout_padding#(lay#, n) | layout_padding#@#n | Set uniform padding on all four sides |
layout_paddings#(lay#, l, t, r, b) | layout_paddings#@#nnnn | Set individual padding (left, top, right, bottom) |
layout_paddingleft(lay#) | layout_paddingleft@# | Get left padding |
layout_paddingleft#(lay#, n) | layout_paddingleft#@#n | Set left padding |
layout_paddingtop(lay#) | layout_paddingtop@# | Get top padding |
layout_paddingtop#(lay#, n) | layout_paddingtop#@#n | Set top padding |
layout_paddingright(lay#) | layout_paddingright@# | Get right padding |
layout_paddingright#(lay#, n) | layout_paddingright#@#n | Set right padding |
layout_paddingbottom(lay#) | layout_paddingbottom@# | Get bottom padding |
layout_paddingbottom#(lay#, n) | layout_paddingbottom#@#n | Set bottom padding |
' 10px padding on all sides layout_padding#(lay#, 10) ' Different padding per side layout_paddings#(lay#, 15, 10, 15, 10)
Visibility & State
| Function | Signature | Description |
|---|---|---|
layout_visible(lay#) | layout_visible@# | Get visibility (0/1) |
layout_visible#(lay#, n) | layout_visible#@#n | Set visibility (hides layout and all children) |
layout_enabled(lay#) | layout_enabled@# | Get enabled state (0/1) |
layout_enabled#(lay#, n) | layout_enabled#@#n | Set enabled state (disables layout and all children) |
layout_opacity(lay#) | layout_opacity@# | Get opacity (0.0–1.0) |
layout_opacity#(lay#, value) | layout_opacity#@#n | Set opacity (affects children too) |
layout_hittest(lay#) | layout_hittest@# | Get hit-test state (0/1) |
layout_hittest#(lay#, n) | layout_hittest#@#n | Enable/disable mouse hit testing |
layout_clipchildren(lay#) | layout_clipchildren@# | Get clip-children mode (0/1) |
layout_clipchildren#(lay#, n) | layout_clipchildren#@#n | Enable/disable clipping of child controls to layout bounds |
layout_locked(lay#) | layout_locked@# | Get locked state (0/1) |
layout_locked#(lay#, n) | layout_locked#@#n | Lock/unlock layout to prevent user interaction |
' 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)
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
| Function | Signature | Description |
|---|---|---|
layout_tag(lay#) | layout_tag@# | Get user-defined integer tag |
layout_tag#(lay#, n) | layout_tag#@#n | Set 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
| Function | Signature | Description |
|---|---|---|
layout_childcount(lay#) | layout_childcount@# | Get the number of child controls |
layout_child#(lay#, index) | layout_child#@#n | Get child control pointer at index (0-based) |
' 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
| Function | Signature | Description |
|---|---|---|
layout_invalidate#(lay#) | layout_invalidate#@# | Force the layout and its children to redraw |
' Force redraw (useful after programmatic changes or with OnPaint) layout_invalidate#(lay#)
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 Setter | Getter | Callback Signature |
|---|---|---|
layout_onclick#(lay#, func$) | layout_onclick$(lay#) | function name(sender#) |
layout_ondblclick#(lay#, func$) | layout_ondblclick$(lay#) | function name(sender#) |
Mouse Events
| Event Setter | Getter | Callback 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$) |
delta value: positive for scroll up, negative for scroll down.Other Events
| Event Setter | Getter | Callback Signature | When 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 |
layout_invalidate# to trigger a repaint. Not available in PanelLib.Layout vs Panel
| Feature | Layout | Panel |
|---|---|---|
| Visible | No (fully transparent) | Yes (has background) |
| Background | None | Platform default |
| OnPaint event | ✅ Yes (custom drawing) | ❌ No |
| Use Case | Invisible grouping / custom drawing | Visible sections / cards |
| Performance | Lighter weight | Slightly heavier |
| Clipping | Yes (layout_clipchildren#) | Yes (panel_clipchildren#) |
| Padding | Yes | Yes |
| Children access | Yes (childcount / child#) | Yes (childcount / child#) |
| Drag events | No | Yes |
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
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
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
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
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
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
| Practice | Why |
|---|---|
Use layout_align# for responsive layouts | Controls resize automatically with their parent |
Enable layout_clipchildren# | Prevents children from drawing outside layout bounds |
| Nest layouts for complex structures | Combine alignment modes (top + left + client) for multi-section UIs |
| Use padding for content inset | Controls gap between layout edge and children |
| Use margins for spacing between layouts | Controls gap between sibling layouts |
| Add aligned layouts in order: edges first, client last | Top/Bottom/Left/Right layouts claim space; Client fills what remains |
| Use Layout instead of Panel when no background is needed | Layouts are lighter weight and fully transparent |
Use layout_onpaint# for custom drawing | Unique to Layout — enables owner-drawn content |
Use layout_onresized# over layout_onresize# | OnResized fires once when complete, avoiding per-pixel recalculations |
Quick Reference
| Function | Signature | Description |
|---|---|---|
layout_error / errormsg$ / strerror$ / clearerror | various | Error handling (4) |
layout#(parent#[, w, h] | [, x, y, w, h]) | various | Create (3 overloads) |
layout_free(lay#) | layout_free@# | Destroy (and all children) |
layout_x/y/width/height (get/set) / bounds# / move# / size# | various | Position & size (14) |
layout_align / margin# / margins# / margin[left/top/right/bottom] | various | Alignment & margins (12) |
layout_padding# / paddings# / padding[left/top/right/bottom] | various | Padding (10) |
layout_visible / enabled / opacity / hittest / clipchildren / locked | various | Visibility & state (12) |
layout_tag / parent# / bringtofront# / sendtoback# | various | Tag & parent (6) |
layout_childcount / child# | various | Children (2) |
layout_invalidate# | layout_invalidate#@# | Force redraw (1) |
layout_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onpaint/onresize/onresized | various | Events set+get (19) |
layout_clearcallbacks# | layout_clearcallbacks#@# | Disconnect all events |
85 functions. Part of the Plan9Basic GUI library system.