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.
| Category | Count | Description |
|---|---|---|
| Error Handling | 4 | panel_error, errormsg$, strerror$, clearerror |
| Creation & Destruction | 4 | panel# (3 overloads), panel_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 | 26 | 12 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 |
|---|---|---|
panel_error() | panel_error@ | Last error code (0 = no error) |
panel_errormsg$() | panel_errormsg$@ | Last error message as string |
panel_strerror$(code) | panel_strerror$@n | Description for a given error code |
panel_clearerror() | panel_clearerror@ | Clear the error state |
Numeric Values Reference
Control Alignment panel_align#
| Value | Description | Use Case |
|---|---|---|
| 0 | None (absolute positioning) | Manual x, y placement |
| 1 | Top | Headers, toolbars |
| 2 | Left | Sidebars, navigation |
| 3 | Right | Side panels, property editors |
| 4 | Bottom | Status bars, footers |
| 9 | Client (fill parent) | Main content area |
| 11 | Center | Centered dialog panels |
Creation & Destruction
| Function | Signature | Description |
|---|---|---|
panel#(parent#) | panel#@# | Create panel with default size (100×100) |
panel#(parent#, w, h) | panel#@#nn | Create with specified size (at 0, 0) |
panel#(parent#, x, y, w, h) | panel#@#nnnn | Create with position and size |
panel_free(pnl#) | panel_free@# | Destroy panel and all its child controls |
' 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)
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
| Function | Signature | Description |
|---|---|---|
panel_x(pnl#) | panel_x@# | Get X position |
panel_x#(pnl#, x) | panel_x#@#n | Set X position |
panel_y(pnl#) | panel_y@# | Get Y position |
panel_y#(pnl#, y) | panel_y#@#n | Set Y position |
panel_width(pnl#) | panel_width@# | Get width |
panel_width#(pnl#, w) | panel_width#@#n | Set width |
panel_height(pnl#) | panel_height@# | Get height |
panel_height#(pnl#, h) | panel_height#@#n | Set height |
panel_bounds#(pnl#, x, y, w, h) | panel_bounds#@#nnnn | Set position and size in one call |
panel_move#(pnl#, x, y) | panel_move#@#nn | Set position only |
panel_size#(pnl#, w, h) | panel_size#@#nn | Set 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.
| Function | Signature | Description |
|---|---|---|
panel_align(pnl#) | panel_align@# | Get control alignment |
panel_align#(pnl#, n) | panel_align#@#n | Set alignment (0=none, 1=top, 2=left, 3=right, 4=bottom, 9=client, 11=center) |
panel_margin#(pnl#, n) | panel_margin#@#n | Set uniform margin on all four sides |
panel_margins#(pnl#, l, t, r, b) | panel_margins#@#nnnn | Set individual margins (left, top, right, bottom) |
panel_marginleft(pnl#) | panel_marginleft@# | Get left margin |
panel_marginleft#(pnl#, n) | panel_marginleft#@#n | Set left margin |
panel_margintop(pnl#) | panel_margintop@# | Get top margin |
panel_margintop#(pnl#, n) | panel_margintop#@#n | Set top margin |
panel_marginright(pnl#) | panel_marginright@# | Get right margin |
panel_marginright#(pnl#, n) | panel_marginright#@#n | Set right margin |
panel_marginbottom(pnl#) | panel_marginbottom@# | Get bottom margin |
panel_marginbottom#(pnl#, n) | panel_marginbottom#@#n | Set bottom margin |
' 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.
| Function | Signature | Description |
|---|---|---|
panel_padding#(pnl#, n) | panel_padding#@#n | Set uniform padding on all four sides |
panel_paddings#(pnl#, l, t, r, b) | panel_paddings#@#nnnn | Set individual padding (left, top, right, bottom) |
panel_paddingleft(pnl#) | panel_paddingleft@# | Get left padding |
panel_paddingleft#(pnl#, n) | panel_paddingleft#@#n | Set left padding |
panel_paddingtop(pnl#) | panel_paddingtop@# | Get top padding |
panel_paddingtop#(pnl#, n) | panel_paddingtop#@#n | Set top padding |
panel_paddingright(pnl#) | panel_paddingright@# | Get right padding |
panel_paddingright#(pnl#, n) | panel_paddingright#@#n | Set right padding |
panel_paddingbottom(pnl#) | panel_paddingbottom@# | Get bottom padding |
panel_paddingbottom#(pnl#, n) | panel_paddingbottom#@#n | Set bottom padding |
' 10px padding on all sides panel_padding#(pnl#, 10) ' Different padding per side panel_paddings#(pnl#, 15, 10, 15, 10)
Visibility & State
| Function | Signature | Description |
|---|---|---|
panel_visible(pnl#) | panel_visible@# | Get visibility (0/1) |
panel_visible#(pnl#, n) | panel_visible#@#n | Set visibility (hides panel and all children) |
panel_enabled(pnl#) | panel_enabled@# | Get enabled state (0/1) |
panel_enabled#(pnl#, n) | panel_enabled#@#n | Set enabled state (disables panel and all children) |
panel_opacity(pnl#) | panel_opacity@# | Get opacity (0.0–1.0) |
panel_opacity#(pnl#, value) | panel_opacity#@#n | Set opacity (affects panel and all children) |
panel_hittest(pnl#) | panel_hittest@# | Get hit-test state (0/1) |
panel_hittest#(pnl#, n) | panel_hittest#@#n | Enable/disable mouse hit testing |
panel_clipchildren(pnl#) | panel_clipchildren@# | Get clip children mode (0/1) |
panel_clipchildren#(pnl#, n) | panel_clipchildren#@#n | Enable/disable clipping of child controls to panel bounds |
panel_locked(pnl#) | panel_locked@# | Get locked state (0/1) |
panel_locked#(pnl#, n) | panel_locked#@#n | Lock/unlock panel to prevent user interaction |
' 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)
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
| Function | Signature | Description |
|---|---|---|
panel_tag(pnl#) | panel_tag@# | Get user-defined integer tag |
panel_tag#(pnl#, n) | panel_tag#@#n | Set 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
| Function | Signature | Description |
|---|---|---|
panel_childcount(pnl#) | panel_childcount@# | Get the number of child controls |
panel_child#(pnl#, index) | panel_child#@#n | Get child control pointer at index (0-based) |
' 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
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
| Function | Signature | Description |
|---|---|---|
panel_invalidate#(pnl#) | panel_invalidate#@# | Force the panel and its children to redraw |
' 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 Setter | Getter | Callback Signature |
|---|---|---|
panel_onclick#(pnl#, func$) | panel_onclick$(pnl#) | function name(sender#) |
panel_ondblclick#(pnl#, func$) | panel_ondblclick$(pnl#) | function name(sender#) |
Mouse Events
| Event Setter | Getter | Callback 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$) |
delta value: positive for scroll up, negative for scroll down.Other Events
| Event Setter | Getter | Callback Signature | When 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 |
Layout vs Panel
| Feature | Layout | Panel |
|---|---|---|
| Visible | No (fully transparent) | Yes (has background) |
| Background | None | Platform default |
| Use Case | Invisible grouping / spacing | Visible sections / cards / toolbars |
| Performance | Lighter | Slightly heavier |
| Clipping | Yes | Yes (panel_clipchildren#) |
| Padding | Yes | Yes |
| Events | Limited | Full 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
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
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
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
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
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
| Practice | Why |
|---|---|
Use panel_align# for responsive layouts | Panels resize automatically with their parent |
Enable panel_clipchildren# | Prevents children from drawing outside panel bounds |
| Use Panels for visible sections | Headers, footers, sidebars, cards, toolbars |
| Nest panels for complex layouts | Combine alignment modes for multi-section UIs |
| Use margins for spacing between panels | Controls gap between siblings |
| Use padding for content inset | Controls 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 needed | Layouts are lighter weight and fully transparent |
Quick Reference
| Function | Signature | Description |
|---|---|---|
panel_error / errormsg$ / strerror$ / clearerror | various | Error handling (4) |
panel#(parent#[, w, h] | [, x, y, w, h]) | various | Create (3 overloads) |
panel_free(pnl#) | panel_free@# | Destroy (and all children) |
panel_x/y/width/height (get/set) / bounds# / move# / size# | various | Position & size (14) |
panel_align / margin# / margins# / margin[left/top/right/bottom] | various | Alignment & margins (12) |
panel_padding# / paddings# / padding[left/top/right/bottom] | various | Padding (10) |
panel_visible / enabled / opacity / hittest / clipchildren / locked | various | Visibility & state (12) |
panel_tag / parent# / bringtofront# / sendtoback# | various | Tag & parent (6) |
panel_childcount / child# | various | Children (2) |
panel_invalidate# | panel_invalidate#@# | Force redraw (1) |
panel_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onresize/onresized/ondragenter/over/drop/leave | various | Events set+get (25) |
panel_clearcallbacks# | panel_clearcallbacks#@# | Disconnect all events |
91 functions. Part of the Plan9Basic GUI library system.