PanelLib — Panel Container Library

Visible container controls for organizing and grouping other controls in Plan9Basic programs. Panels have a background and can be styled, unlike the invisible Layout control. Features include alignment-based responsive layouts, margins, padding, clipping, child management, locking, mouse wheel events, drag support, and a comprehensive event system. 91 functions.

CategoryCountDescription
Error Handling4panel_error, errormsg$, strerror$, clearerror
Creation & Destruction4panel# (3 overloads), panel_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#
Events2612 event types × set/get + clearcallbacks#

Cross-Platform Support

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

Error Handling

FunctionSignatureDescription
panel_error()panel_error@Last error code (0 = no error)
panel_errormsg$()panel_errormsg$@Last error message as string
panel_strerror$(code)panel_strerror$@nDescription for a given error code
panel_clearerror()panel_clearerror@Clear the error state

Numeric Values Reference

Control Alignment panel_align#

ValueDescriptionUse Case
0None (absolute positioning)Manual x, y placement
1TopHeaders, toolbars
2LeftSidebars, navigation
3RightSide panels, property editors
4BottomStatus bars, footers
9Client (fill parent)Main content area
11CenterCentered dialog panels

Creation & Destruction

FunctionSignatureDescription
panel#(parent#)panel#@#Create panel with default size (100×100)
panel#(parent#, w, h)panel#@#nnCreate with specified size (at 0, 0)
panel#(parent#, x, y, w, h)panel#@#nnnnCreate with position and size
panel_free(pnl#)panel_free@#Destroy panel and all its child controls
╯ plan9basic
' Header panel docked to top
let header# = panel#(frm#, 600, 60)
panel_align#(header#, 1)

' Content panel filling remaining space
let content# = panel#(frm#)
panel_align#(content#, 9)

' Positioned panel
let card# = panel#(frm#, 30, 50, 200, 280)
⚠ Warning: panel_free destroys the panel and all its child controls. Make sure you no longer reference any children after freeing the parent panel.

Position & Size

FunctionSignatureDescription
panel_x(pnl#)panel_x@#Get X position
panel_x#(pnl#, x)panel_x#@#nSet X position
panel_y(pnl#)panel_y@#Get Y position
panel_y#(pnl#, y)panel_y#@#nSet Y position
panel_width(pnl#)panel_width@#Get width
panel_width#(pnl#, w)panel_width#@#nSet width
panel_height(pnl#)panel_height@#Get height
panel_height#(pnl#, h)panel_height#@#nSet height
panel_bounds#(pnl#, x, y, w, h)panel_bounds#@#nnnnSet position and size in one call
panel_move#(pnl#, x, y)panel_move#@#nnSet position only
panel_size#(pnl#, w, h)panel_size#@#nnSet size only

Alignment & Margins

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

FunctionSignatureDescription
panel_align(pnl#)panel_align@#Get control alignment
panel_align#(pnl#, n)panel_align#@#nSet alignment (0=none, 1=top, 2=left, 3=right, 4=bottom, 9=client, 11=center)
panel_margin#(pnl#, n)panel_margin#@#nSet uniform margin on all four sides
panel_margins#(pnl#, l, t, r, b)panel_margins#@#nnnnSet individual margins (left, top, right, bottom)
panel_marginleft(pnl#)panel_marginleft@#Get left margin
panel_marginleft#(pnl#, n)panel_marginleft#@#nSet left margin
panel_margintop(pnl#)panel_margintop@#Get top margin
panel_margintop#(pnl#, n)panel_margintop#@#nSet top margin
panel_marginright(pnl#)panel_marginright@#Get right margin
panel_marginright#(pnl#, n)panel_marginright#@#nSet right margin
panel_marginbottom(pnl#)panel_marginbottom@#Get bottom margin
panel_marginbottom#(pnl#, n)panel_marginbottom#@#nSet bottom margin
╯ plan9basic
' Typical application layout
let header# = panel#(frm#, 600, 60)
panel_align#(header#, 1)       ' Top
panel_margins#(header#, 0, 0, 0, 2)  ' 2px gap below

let sidebar# = panel#(frm#, 200, 400)
panel_align#(sidebar#, 2)      ' Left
panel_margin#(sidebar#, 2)     ' 2px gap all sides

let content# = panel#(frm#)
panel_align#(content#, 9)      ' Client (fill remaining)

Padding

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

FunctionSignatureDescription
panel_padding#(pnl#, n)panel_padding#@#nSet uniform padding on all four sides
panel_paddings#(pnl#, l, t, r, b)panel_paddings#@#nnnnSet individual padding (left, top, right, bottom)
panel_paddingleft(pnl#)panel_paddingleft@#Get left padding
panel_paddingleft#(pnl#, n)panel_paddingleft#@#nSet left padding
panel_paddingtop(pnl#)panel_paddingtop@#Get top padding
panel_paddingtop#(pnl#, n)panel_paddingtop#@#nSet top padding
panel_paddingright(pnl#)panel_paddingright@#Get right padding
panel_paddingright#(pnl#, n)panel_paddingright#@#nSet right padding
panel_paddingbottom(pnl#)panel_paddingbottom@#Get bottom padding
panel_paddingbottom#(pnl#, n)panel_paddingbottom#@#nSet bottom padding
╯ plan9basic
' 10px padding on all sides
panel_padding#(pnl#, 10)

' Different padding per side
panel_paddings#(pnl#, 15, 10, 15, 10)
ⓘ Note: Margins = space outside the panel (between panel and its siblings). Padding = space inside the panel (between panel edge and its children). Both work together to create well-spaced layouts.

Visibility & State

FunctionSignatureDescription
panel_visible(pnl#)panel_visible@#Get visibility (0/1)
panel_visible#(pnl#, n)panel_visible#@#nSet visibility (hides panel and all children)
panel_enabled(pnl#)panel_enabled@#Get enabled state (0/1)
panel_enabled#(pnl#, n)panel_enabled#@#nSet enabled state (disables panel and all children)
panel_opacity(pnl#)panel_opacity@#Get opacity (0.0–1.0)
panel_opacity#(pnl#, value)panel_opacity#@#nSet opacity (affects panel and all children)
panel_hittest(pnl#)panel_hittest@#Get hit-test state (0/1)
panel_hittest#(pnl#, n)panel_hittest#@#nEnable/disable mouse hit testing
panel_clipchildren(pnl#)panel_clipchildren@#Get clip children mode (0/1)
panel_clipchildren#(pnl#, n)panel_clipchildren#@#nEnable/disable clipping of child controls to panel bounds
panel_locked(pnl#)panel_locked@#Get locked state (0/1)
panel_locked#(pnl#, n)panel_locked#@#nLock/unlock panel to prevent user interaction
╯ plan9basic
' Enable clipping so children don't draw outside
panel_clipchildren#(pnl#, 1)

' Make panel semi-transparent
panel_opacity#(pnl#, 0.7)

' Lock panel to prevent interaction
panel_locked#(pnl#, 1)
ⓘ Note: panel_clipchildren# clips child controls to the panel's rectangular bounds. Enable this when children might extend beyond the panel edges (e.g., during animation or scrolling).

Tag & Parent

FunctionSignatureDescription
panel_tag(pnl#)panel_tag@#Get user-defined integer tag
panel_tag#(pnl#, n)panel_tag#@#nSet user-defined integer tag
panel_parent#(pnl#)panel_parent#@#Get parent control pointer
panel_parent#(pnl#, parent#)panel_parent#@##Move panel to a different parent
panel_bringtofront#(pnl#)panel_bringtofront#@#Bring to front of Z-order
panel_sendtoback#(pnl#)panel_sendtoback#@#Send to back of Z-order

Children Management

FunctionSignatureDescription
panel_childcount(pnl#)panel_childcount@#Get the number of child controls
panel_child#(pnl#, index)panel_child#@#nGet child control pointer at index (0-based)
╯ plan9basic
' Iterate all children
let cnt = panel_childcount(pnl#)
for i = 0 to cnt - 1
    let child# = panel_child#(pnl#, i)
    println "Child " + str$(i)
next
ⓘ Note: Child pointers returned by panel_child# are generic control pointers. You can pass them to functions from any control library, but make sure you use the correct library for the actual control type.

Utility

FunctionSignatureDescription
panel_invalidate#(pnl#)panel_invalidate#@#Force the panel and its children to redraw
╯ plan9basic
' Force redraw after programmatic changes
panel_invalidate#(pnl#)

Events

Each event has a setter (panel_onXXX#(pnl#, func$)) and a getter (panel_onXXX$(pnl#)). Use panel_clearcallbacks#(pnl#) to disconnect all callbacks at once.

Click Events

Event SetterGetterCallback Signature
panel_onclick#(pnl#, func$)panel_onclick$(pnl#)function name(sender#)
panel_ondblclick#(pnl#, func$)panel_ondblclick$(pnl#)function name(sender#)

Mouse Events

Event SetterGetterCallback Signature
panel_onmousedown#(pnl#, func$)panel_onmousedown$(pnl#)function name(sender#, button, x, y, shift$)
panel_onmouseup#(pnl#, func$)panel_onmouseup$(pnl#)function name(sender#, button, x, y, shift$)
panel_onmousemove#(pnl#, func$)panel_onmousemove$(pnl#)function name(sender#, x, y, shift$)
panel_onmouseenter#(pnl#, func$)panel_onmouseenter$(pnl#)function name(sender#)
panel_onmouseleave#(pnl#, func$)panel_onmouseleave$(pnl#)function name(sender#)
panel_onmousewheel#(pnl#, func$)panel_onmousewheel$(pnl#)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
panel_onresize#(pnl#, func$)panel_onresize$(pnl#)function name(sender#)Panel is being resized
panel_onresized#(pnl#, func$)panel_onresized$(pnl#)function name(sender#)Panel resize is complete
panel_ondragenter#(pnl#, func$)panel_ondragenter$(pnl#)function name(sender#)Drag enters panel area
panel_ondragover#(pnl#, func$)panel_ondragover$(pnl#)function name(sender#)Drag moves over panel
panel_ondragdrop#(pnl#, func$)panel_ondragdrop$(pnl#)function name(sender#)Item is dropped on panel
panel_ondragleave#(pnl#, func$)panel_ondragleave$(pnl#)function name(sender#)Drag leaves panel area
panel_clearcallbacks#(pnl#)Disconnect all event callbacks
ⓘ Note: OnResize fires continuously during resizing. OnResized fires once when resizing is complete. Use OnResized when you only need the final size.

Layout vs Panel

FeatureLayoutPanel
VisibleNo (fully transparent)Yes (has background)
BackgroundNonePlatform default
Use CaseInvisible grouping / spacingVisible sections / cards / toolbars
PerformanceLighterSlightly heavier
ClippingYesYes (panel_clipchildren#)
PaddingYesYes
EventsLimitedFull mouse + drag events

Use Layout when you need invisible organization. Use Panel when you need visible separation, sections, or styled containers.

Complete Examples

Header + Content Layout

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

' Header panel at top
let header# = panel#(frm#, 600, 60)
panel_align#(header#, 1)

let lblTitle# = label#(header#, "My Application")
label_move#(lblTitle#, 20, 20)
label_fontsize#(lblTitle#, 18)
label_bold#(lblTitle#, 1)

' Content area fills remaining space
let content# = panel#(frm#)
panel_align#(content#, 9)

let lblContent# = label#(content#, "Main content goes here")
label_move#(lblContent#, 200, 150)

form_show(frm#)

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

Sidebar Navigation

╯ sidebar.bas
function OnNav(sender#) local tag, page$
    tag = button_tag(sender#)
    if tag = 1 then page$ = "Dashboard"
    elseif tag = 2 then page$ = "Settings"
    elseif tag = 3 then page$ = "Reports"
    elseif tag = 4 then page$ = "Help"
    endif
    label_text#(lblPage#, page$)
endfunction

let frm# = form#("Sidebar Demo", 800, 500)
form_position#(frm#, 4)

' Sidebar panel
let sidebar# = panel#(frm#, 200, 500)
panel_align#(sidebar#, 2)

let btn1# = button#(sidebar#, "Dashboard", 10, 20, 180, 40)
let btn2# = button#(sidebar#, "Settings", 10, 70, 180, 40)
let btn3# = button#(sidebar#, "Reports", 10, 120, 180, 40)
let btn4# = button#(sidebar#, "Help", 10, 170, 180, 40)
button_tag#(btn1#, 1)
button_tag#(btn2#, 2)
button_tag#(btn3#, 3)
button_tag#(btn4#, 4)
button_onclick#(btn1#, "OnNav")
button_onclick#(btn2#, "OnNav")
button_onclick#(btn3#, "OnNav")
button_onclick#(btn4#, "OnNav")

' Main content
let main# = panel#(frm#)
panel_align#(main#, 9)

let lblPage# = label#(main#, "Dashboard")
label_move#(lblPage#, 250, 200)
label_fontsize#(lblPage#, 24)

form_show(frm#)

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

Card Layout

╯ cards.bas
let frm# = form#("Card Layout", 700, 400)
form_position#(frm#, 4)

let card1# = panel#(frm#, 30, 50, 200, 280)
let card2# = panel#(frm#, 250, 50, 200, 280)
let card3# = panel#(frm#, 470, 50, 200, 280)

' Card 1
let lbl1# = label#(card1#, "Card 1")
label_move#(lbl1#, 70, 20)
label_fontsize#(lbl1#, 16)
label_bold#(lbl1#, 1)
let txt1# = label#(card1#, "Description for card one goes here.")
label_bounds#(txt1#, 10, 60, 180, 150)
label_wordwrap#(txt1#, 1)
button#(card1#, "View", 50, 230, 100, 35)

' Card 2
let lbl2# = label#(card2#, "Card 2")
label_move#(lbl2#, 70, 20)
label_fontsize#(lbl2#, 16)
label_bold#(lbl2#, 1)
let txt2# = label#(card2#, "Description for card two goes here.")
label_bounds#(txt2#, 10, 60, 180, 150)
label_wordwrap#(txt2#, 1)
button#(card2#, "View", 50, 230, 100, 35)

' Card 3
let lbl3# = label#(card3#, "Card 3")
label_move#(lbl3#, 70, 20)
label_fontsize#(lbl3#, 16)
label_bold#(lbl3#, 1)
let txt3# = label#(card3#, "Description for card three goes here.")
label_bounds#(txt3#, 10, 60, 180, 150)
label_wordwrap#(txt3#, 1)
button#(card3#, "View", 50, 230, 100, 35)

form_show(frm#)

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

Collapsible Panel

╯ collapsible.bas
let expanded = 1

function OnToggle(sender#)
    if expanded = 1 then
        panel_visible#(pnl#, 0)
        button_text#(btnToggle#, "Show Panel")
        expanded = 0
    else
        panel_visible#(pnl#, 1)
        button_text#(btnToggle#, "Hide Panel")
        expanded = 1
    endif
endfunction

let frm# = form#("Collapsible Panel", 500, 400)
form_position#(frm#, 4)

let btnToggle# = button#(frm#, "Toggle Panel", 20, 20, 120, 35)

let pnl# = panel#(frm#, 20, 70, 460, 200)
let lbl# = label#(pnl#, "This content can be hidden")
label_move#(lbl#, 150, 90)

button_onclick#(btnToggle#, "OnToggle")

form_show(frm#)

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

Status Bar

╯ statusbar.bas
let frm# = form#("Status Bar Demo", 600, 400)
form_position#(frm#, 4)

' Main content area
let main# = panel#(frm#)
panel_align#(main#, 9)
let lbl# = label#(main#, "Main Application Area")
label_move#(lbl#, 220, 170)

' Status bar at bottom
let status# = panel#(frm#, 600, 30)
panel_align#(status#, 4)

let lblStatus# = label#(status#, "Ready")
label_move#(lblStatus#, 10, 5)

let lblTime# = label#(status#, "")
label_move#(lblTime#, 500, 5)
label_text#(lblTime#, formatdatetime$("hh:nn:ss", now()))

form_show(frm#)

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

Best Practices

PracticeWhy
Use panel_align# for responsive layoutsPanels resize automatically with their parent
Enable panel_clipchildren#Prevents children from drawing outside panel bounds
Use Panels for visible sectionsHeaders, footers, sidebars, cards, toolbars
Nest panels for complex layoutsCombine alignment modes for multi-section UIs
Use margins for spacing between panelsControls gap between siblings
Use padding for content insetControls gap between panel edge and children
Use panel_onresized# over panel_onresize#OnResized fires once when resize completes, avoiding costly per-pixel recalculations
Use Layout instead of Panel when no background is neededLayouts are lighter weight and fully transparent

Quick Reference

FunctionSignatureDescription
panel_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
panel#(parent#[, w, h] | [, x, y, w, h])variousCreate (3 overloads)
panel_free(pnl#)panel_free@#Destroy (and all children)
panel_x/y/width/height (get/set) / bounds# / move# / size#variousPosition & size (14)
panel_align / margin# / margins# / margin[left/top/right/bottom]variousAlignment & margins (12)
panel_padding# / paddings# / padding[left/top/right/bottom]variousPadding (10)
panel_visible / enabled / opacity / hittest / clipchildren / lockedvariousVisibility & state (12)
panel_tag / parent# / bringtofront# / sendtoback#variousTag & parent (6)
panel_childcount / child#variousChildren (2)
panel_invalidate#panel_invalidate#@#Force redraw (1)
panel_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onresize/onresized/ondragenter/over/drop/leavevariousEvents set+get (25)
panel_clearcallbacks#panel_clearcallbacks#@#Disconnect all events

91 functions. Part of the Plan9Basic GUI library system.