ScrollBoxLib — Vertical Scroll Box Library

Vertically-scrolling container controls for Plan9Basic programs. A scroll box holds child controls that extend beyond the visible area and provides automatic vertical scrolling. Children are added to the scroll box as their parent and scroll with it transparently. 25 functions.

CategoryCountDescription
Error Handling3scrollbox_error, scrollbox_strerror$, scrollbox_clearerror
Creation & Destruction3scrollbox# (2 overloads), scrollbox_free
Position & Size7x, y (get), move#, width, height (get/set)
Alignment2align (get/set) — full FireMonkey TAlignLayout (20 values)
Visibility & Opacity4visible, opacity (get/set)
Tag2tag (get/set)
ScrollBox-Specific4showscrollbars (get/set), contentwidth, contentheight

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
Linux✅ Full SupportGTK-based
Android✅ Full SupportTouch-optimized scrolling
ⓘ Note: On touch platforms (Android, iOS) scrolling is handled natively with momentum and deceleration. The scrollbox_showscrollbars# setting controls whether the scroll indicator is drawn, but scrolling always works regardless of this setting.

Error Handling

FunctionSignatureDescription
scrollbox_error()scrollbox_error@Last error code (0 = no error)
scrollbox_clearerror()scrollbox_clearerror@Clear the error state
scrollbox_strerror$(code)scrollbox_strerror$@nDescription for a given error code

Error Codes

CodeMeaning
0No error
1Invalid or nil scroll box pointer
2Invalid parent control
3Invalid value
4Scroll box creation failed
╯ plan9basic
let sb# = scrollbox#(frm#)
if scrollbox_error() <> 0 then
    println "Error: " + scrollbox_strerror$(scrollbox_error())
endif

Alignment Values

ScrollBoxLib exposes the full FireMonkey TAlignLayout enumeration. The most commonly used values for scroll boxes are 9 (Client) for filling the available area and 0 (None) for manual positioning.

ValueNameDescriptionTypical Use
0NoneManual positioning (x, y)Fixed position, overlays
1TopAlign to top of parentTop-docked scroll lists
2LeftAlign to left of parentScrollable sidebars, nav menus
3RightAlign to right of parentScrollable property panels
4BottomAlign to bottom of parentBottom-docked scroll areas
5MostTopAbove all Top-aligned controlsTopmost priority positioning
6MostBottomBelow all Bottom-aligned controlsBottommost priority positioning
7MostLeftBefore all Left-aligned controlsLeftmost priority positioning
8MostRightAfter all Right-aligned controlsRightmost priority positioning
9ClientFill all remaining spaceMain content area (most common)
10ContentsSize to fit contentAuto-sizing scroll containers
11CenterCenter in parent (both axes)Centered floating boxes
12VertCenterCenter vertically, full widthVertically centered bands
13HorzCenterCenter horizontally, full heightHorizontally centered columns
14HorizontalStretch full width, manual heightFull-width fixed-height bars
15VerticalStretch full height, manual widthFull-height fixed-width columns
16ScaleScale proportionally with parentProportional layouts
17FitFit within parent, preserve ratioRatio-constrained containers
18FitLeftFit and align to leftLeft-biased fit layouts
19FitRightFit and align to rightRight-biased fit layouts
╯ plan9basic
' Common alignment constants
let ALIGN_NONE   = 0
let ALIGN_LEFT   = 2
let ALIGN_RIGHT  = 3
let ALIGN_CLIENT = 9

Creation & Destruction

FunctionSignatureDescription
scrollbox#(parent#)scrollbox#@#Create scroll box filling the parent (Client alignment)
scrollbox#(parent#, x, y, w, h)scrollbox#@#nnnnCreate at a specific position and size (no alignment)
scrollbox_free(sb#)scrollbox_free@#Destroy scroll box and free all resources
╯ plan9basic
' Fill the entire form
let sb# = scrollbox#(frm#)

' At a specific position and size
let sb# = scrollbox#(frm#, 10, 60, 300, 400)

' Children become scrollable automatically
let lbl# = label#(sb#, "Line 1", 10, 10)

' Cleanup
scrollbox_free(sb#)
ⓘ Note: The single-argument overload scrollbox#(parent#) creates the scroll box with Client alignment, making it fill all available space in the parent. The five-argument overload sets alignment to None, allowing manual positioning. In both cases, any control created with sb# as its parent will scroll with the container.
⚠ Warning: scrollbox_free destroys the scroll box and all its child controls. Do not reference any children after freeing the parent scroll box.

Position & Size

Position getters return the current coordinates. Use scrollbox_move# to reposition the scroll box as a single atomic operation. Width and height each have independent getters and setters. Note that position and size only take effect when alignment is set to None (0) — aligned controls are positioned automatically by the parent.

FunctionSignatureDescription
scrollbox_x(sb#)scrollbox_x@#Get X position
scrollbox_y(sb#)scrollbox_y@#Get Y position
scrollbox_move#(sb#, x, y)scrollbox_move#@#nnSet position (X and Y together)
scrollbox_width(sb#)scrollbox_width@#Get width
scrollbox_width#(sb#, w)scrollbox_width#@#nSet width
scrollbox_height(sb#)scrollbox_height@#Get height
scrollbox_height#(sb#, h)scrollbox_height#@#nSet height
╯ plan9basic
' Create with manual positioning
let sb# = scrollbox#(frm#, 20, 60, 300, 400)

' Reposition and resize
scrollbox_move#(sb#, 10, 50)
scrollbox_width#(sb#, 320)
scrollbox_height#(sb#, 450)

println "At: " + str$(scrollbox_x(sb#)) + ", " + str$(scrollbox_y(sb#))
println "Size: " + str$(scrollbox_width(sb#)) + " x " + str$(scrollbox_height(sb#))
ⓘ Note: There is no compound scrollbox_bounds# setter. To set both position and size at creation time, use the five-argument scrollbox#(parent#, x, y, w, h) overload instead.

Alignment

When alignment is set to any value other than None (0), the scroll box is automatically positioned and sized by its parent container. This is the recommended approach for most layouts. See the Alignment Values section for the full list of supported modes.

FunctionSignatureDescription
scrollbox_align(sb#)scrollbox_align@#Get the current alignment mode
scrollbox_align#(sb#, n)scrollbox_align#@#nSet alignment (0–19, see Alignment Values)
╯ plan9basic
' Fill entire form (most common)
scrollbox_align#(sb#, 9)

' Left sidebar, 220px wide
scrollbox_width#(sb#, 220)
scrollbox_align#(sb#, 2)

' Right panel, 200px wide
scrollbox_width#(sb#, 200)
scrollbox_align#(sb#, 3)

println "Alignment: " + str$(scrollbox_align(sb#))

Visibility & Opacity

FunctionSignatureDescription
scrollbox_visible(sb#)scrollbox_visible@#Get visibility (0/1)
scrollbox_visible#(sb#, v)scrollbox_visible#@#nSet visibility (0=hidden, 1=visible). Hides all children too.
scrollbox_opacity(sb#)scrollbox_opacity@#Get opacity (0.0–1.0)
scrollbox_opacity#(sb#, o)scrollbox_opacity#@#nSet opacity (0.0=transparent, 1.0=opaque). Affects all children.
╯ plan9basic
' Toggle a panel section visible/hidden
scrollbox_visible#(sb#, 0)    ' Hide scroll box and all children
scrollbox_visible#(sb#, 1)    ' Show again

' Fade effect
scrollbox_opacity#(sb#, 0.4)  ' 40% opacity (dim the entire list)
scrollbox_opacity#(sb#, 1.0)  ' Fully opaque

Tag

An integer tag value attached to the scroll box for user-defined purposes. Useful for identifying which scroll box triggered a callback when multiple scroll boxes share a handler, or for storing state without external variables.

FunctionSignatureDescription
scrollbox_tag(sb#)scrollbox_tag@#Get the user-defined integer tag
scrollbox_tag#(sb#, n)scrollbox_tag#@#nSet the user-defined integer tag
╯ plan9basic
' Tag scroll boxes to identify which page they belong to
scrollbox_tag#(sbDashboard#, 1)
scrollbox_tag#(sbSettings#, 2)
scrollbox_tag#(sbReports#, 3)

function GetPage(sb#) local tag
    tag = scrollbox_tag(sb#)
    if tag = 1 then println "Dashboard"
    elseif tag = 2 then println "Settings"
    elseif tag = 3 then println "Reports"
    endif
endfunction

ScrollBox-Specific Properties

These functions control the scroll bar indicator and allow querying the total extent of the scrollable content region.

FunctionSignatureDescription
scrollbox_showscrollbars(sb#)scrollbox_showscrollbars@#Get scroll bar visibility (0/1)
scrollbox_showscrollbars#(sb#, v)scrollbox_showscrollbars#@#nShow (1) or hide (0) the scroll bar indicator
scrollbox_contentwidth(sb#)scrollbox_contentwidth@#Get total scrollable content width
scrollbox_contentheight(sb#)scrollbox_contentheight@#Get total scrollable content height
╯ plan9basic
' Show the scroll bar indicator
scrollbox_showscrollbars#(sb#, 1)

' Hide it for a cleaner touch-friendly look
scrollbox_showscrollbars#(sb#, 0)

' Query how far the content actually extends
let cw = scrollbox_contentwidth(sb#)
let ch = scrollbox_contentheight(sb#)
println "Content: " + str$(cw) + " x " + str$(ch)

' Determine whether scrolling will actually occur
if scrollbox_contentheight(sb#) > scrollbox_height(sb#) then
    println "Content overflows — scroll bar is useful"
endif
ⓘ Note: scrollbox_contentwidth and scrollbox_contentheight report the ContentBounds of the scroll box — the bounding rectangle of all child controls combined. If no children have been added, both values are 0. These values update automatically as children are added or resized.

Complete Examples

Scrollable Item List

╯ scrolllist.bas
let frm# = form#("Item List", 400, 500)
form_position#(frm#, 4)

' Scroll box fills the entire form
let sb# = scrollbox#(frm#)
scrollbox_showscrollbars#(sb#, 1)

' Add 60 labels — they extend far beyond the visible area
let i = 0
for i = 1 to 60
    let lbl# = label#(sb#, "Item " + str$(i), 10, (i - 1) * 30)
next

form_show(frm#)

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

Dynamic Content Builder

╯ dynamic.bas
let itemCount = 0
let sb# = Pointer#(0)

function OnAdd(sender#) local y
    itemCount = itemCount + 1
    y = (itemCount - 1) * 36
    let lbl# = label#(sb#, str$(itemCount) + ". Entry added", 12, y)
    label_fontsize#(lbl#, 13)
endfunction

let frm# = form#("Dynamic List", 420, 520)
form_position#(frm#, 4)

' Toolbar at top
let toolbar# = panel#(frm#, 420, 48)
panel_align#(toolbar#, 1)
let btnAdd# = button#(toolbar#, "+ Add Item", 8, 7, 120, 34)
button_onclick#(btnAdd#, "OnAdd")

' Scroll box fills remaining space
sb# = scrollbox#(frm#)
scrollbox_showscrollbars#(sb#, 1)

form_show(frm#)

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

Scrollable Sidebar Navigation

╯ sidebar.bas
let lblPage# = Pointer#(0)

function OnNavClick(sender#) local tag
    tag = button_tag(sender#)
    label_text#(lblPage#, "Section " + str$(tag))
endfunction

let frm# = form#("Sidebar Demo", 720, 540)
form_position#(frm#, 4)

' Scrollable left sidebar — 15 nav buttons, more than fit in 540px
let sb# = scrollbox#(frm#, 0, 0, 210, 540)
scrollbox_align#(sb#, 2)             ' Align left
scrollbox_showscrollbars#(sb#, 0)    ' Hide scroll bar for clean look

let i = 0
for i = 1 to 15
    let btn# = button#(sb#, "Section " + str$(i), 8, (i - 1) * 52 + 8, 192, 44)
    button_tag#(btn#, i)
    button_onclick#(btn#, "OnNavClick")
next

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

lblPage# = label#(main#, "Select a section")
label_move#(lblPage#, 160, 240)
label_fontsize#(lblPage#, 18)

form_show(frm#)

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

Content Size Query

╯ contentsize.bas
let sb# = Pointer#(0)
let lblInfo# = Pointer#(0)

function OnMeasure(sender#) local cw, ch, overflow$
    cw = scrollbox_contentwidth(sb#)
    ch = scrollbox_contentheight(sb#)
    if ch > scrollbox_height(sb#) then
        overflow$ = " [scrollable]"
    else
        overflow$ = " [fits]"
    endif
    label_text#(lblInfo#, str$(cw) + " x " + str$(ch) + overflow$)
endfunction

let frm# = form#("Content Size", 560, 440)
form_position#(frm#, 4)

let toolbar# = panel#(frm#, 560, 48)
panel_align#(toolbar#, 1)
let btnMeasure# = button#(toolbar#, "Measure Content", 8, 7, 160, 34)
button_onclick#(btnMeasure#, "OnMeasure")
lblInfo# = label#(toolbar#, "click Measure", 185, 15)

sb# = scrollbox#(frm#)
scrollbox_showscrollbars#(sb#, 1)

let i = 0
for i = 1 to 40
    let lbl# = label#(sb#, "Row " + str$(i) + "  —  some content here", 12, (i - 1) * 28)
next

form_show(frm#)

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

Best Practices

PracticeWhy
Use scrollbox#(parent#) (single arg) for main content areasAutomatically applies Client alignment — fills all available space without extra calls
Add children directly to the scroll box pointerAny control created with sb# as parent scrolls automatically — no extra configuration needed
Use scrollbox_align# instead of manual positioning where possibleAlignment adapts to window resize and different screen sizes across platforms
Set scrollbox_showscrollbars#(sb#, 0) for touch-based UIsTouch platforms scroll natively without a visible scrollbar; hiding it gives a cleaner, modern look
Call scrollbox_contentheight to check overflow before showing the scroll barAvoid showing a scroll bar when there is nothing to scroll
Initialize scroll box pointers with Pointer#(0) before callbacksCallbacks may fire before the scroll box is assigned; a nil-initialized variable prevents crashes
Space child controls consistently (e.g. (i - 1) * rowHeight)Uniform row spacing makes content predictable and avoids overlap when adding items dynamically
Do not reference children after calling scrollbox_freescrollbox_free destroys child controls too; dangling pointers will cause runtime errors

Quick Reference

FunctionSignatureDescription
scrollbox_error / clearerror / strerror$variousError handling (3)
scrollbox#(parent#)scrollbox#@#Create, Client alignment
scrollbox#(parent#, x, y, w, h)scrollbox#@#nnnnCreate at bounds, no alignment
scrollbox_free(sb#)scrollbox_free@#Destroy (and all children)
scrollbox_x / scrollbox_yscrollbox_x@#, scrollbox_y@#Get position (2)
scrollbox_move#(sb#, x, y)scrollbox_move#@#nnSet position
scrollbox_width / width#scrollbox_width@#, scrollbox_width#@#nWidth get/set
scrollbox_height / height#scrollbox_height@#, scrollbox_height#@#nHeight get/set
scrollbox_align / align#scrollbox_align@#, scrollbox_align#@#nAlignment get/set (0–19)
scrollbox_visible / visible#scrollbox_visible@#, scrollbox_visible#@#nVisibility get/set
scrollbox_opacity / opacity#scrollbox_opacity@#, scrollbox_opacity#@#nOpacity get/set (0.0–1.0)
scrollbox_tag / tag#scrollbox_tag@#, scrollbox_tag#@#nUser tag get/set
scrollbox_showscrollbars / showscrollbars#scrollbox_showscrollbars@#, scrollbox_showscrollbars#@#nScroll bar indicator get/set
scrollbox_contentwidth(sb#)scrollbox_contentwidth@#Total content width (read-only)
scrollbox_contentheight(sb#)scrollbox_contentheight@#Total content height (read-only)

25 functions. Part of the Plan9Basic GUI library system.