CalloutRectangleLib — Callout Shape Library

Rectangular shapes with a pointer (tail) extending from one side — commonly used for speech bubbles, tooltips, annotations, and informational popups. Features configurable pointer position, length, width, and offset along any edge; rounded corners; fill and stroke styling; rotation; and a complete mouse event system. 94 functions.

CategoryCountDescription
Error Handling4callout_error, errormsg$, strerror$, clearerror
Creation & Destruction4callout# (3 overloads), callout_free
Callout Properties8calloutposition, calloutlength, calloutwidth, calloutoffset (get/set)
Rounded Corners6xradius, yradius (get/set), corners#
Fill3fill$ (get), fill# (set), fillnone#
Stroke11stroke$ / stroke# / strokenone#, thickness, dash, cap, join (get/set)
Position & Size14x, y, width, height (get/set), bounds#, move#, size#
Visual Properties8visible, enabled, opacity, hittest, rotation (get/set), invalidate#
Alignment & Margins12align (get/set), margin#, margins#, marginleft/top/right/bottom (get/set)
Tag & Parent6tag (get/set), parent# (get/set), bringtofront#, sendtoback#
Events188 event types × set/get + onmousewheel + clearcallbacks#

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
Linux✅ Full SupportGTK-based
Android✅ Full SupportTouch-friendly

Error Handling

FunctionSignatureDescription
callout_error()callout_error@Last error code (0 = no error)
callout_errormsg$()callout_errormsg$@Last error message as string
callout_strerror$(code)callout_strerror$@nDescription for a given error code
callout_clearerror()callout_clearerror@Clear the error state

Numeric Values Reference

Callout Position callout_calloutposition#

ValuePositionPointer PointsUse Case
0TopUpward from top edgeTooltip below a control
1LeftLeft from left edgeAnnotation to the right
2BottomDownward from bottom edgeSpeech bubble above a character
3RightRight from right edgeAnnotation to the left

Stroke Dash Style callout_strokedash#

ValueStyle
0Solid
1Dash
2Dot
3DashDot
4DashDotDot

Stroke Cap Style callout_strokecap#

ValueStyle
0Flat
1Round

Stroke Join Style callout_strokejoin#

ValueStyle
0Miter (sharp corners)
1Round
2Bevel (cut corners)

Control Alignment callout_align#

ValueDescription
0None (absolute positioning)
1Top
2Left
3Right
4Bottom
9Client (fill parent)
11Center

Creation & Destruction

FunctionSignatureDescription
callout#(parent#)callout#@#Create callout with default size
callout#(parent#, w, h)callout#@#nnCreate with specified size
callout#(parent#, x, y, w, h)callout#@#nnnnCreate with position and size
callout_free(cal#)callout_free@#Destroy callout shape
╯ plan9basic
let cal# = callout#(frm#, 50, 50, 250, 100)
callout_fill#(cal#, "#FFFFFF")
callout_stroke#(cal#, "#2c3e50")
callout_strokethickness#(cal#, 2)

Callout-Specific Properties

These properties control the pointer (tail) that makes a callout unique. The pointer extends from one edge of the rectangle.

FunctionSignatureDescription
callout_calloutposition(cal#)callout_calloutposition@#Get pointer side (0=Top, 1=Left, 2=Bottom, 3=Right)
callout_calloutposition#(cal#, n)callout_calloutposition#@#nSet which side the pointer extends from
callout_calloutlength(cal#)callout_calloutlength@#Get pointer length (how far it extends)
callout_calloutlength#(cal#, n)callout_calloutlength#@#nSet pointer length
callout_calloutwidth(cal#)callout_calloutwidth@#Get pointer base width
callout_calloutwidth#(cal#, n)callout_calloutwidth#@#nSet pointer base width
callout_calloutoffset(cal#)callout_calloutoffset@#Get pointer position along the edge
callout_calloutoffset#(cal#, n)callout_calloutoffset#@#nSet pointer offset along the edge
╯ plan9basic
' Speech bubble pointing down
callout_calloutposition#(cal#, 2)  ' Bottom
callout_calloutlength#(cal#, 20)   ' 20px pointer
callout_calloutwidth#(cal#, 15)    ' 15px base width
callout_calloutoffset#(cal#, 30)   ' 30px from left edge

' Tooltip pointing up
callout_calloutposition#(cal#, 0)  ' Top
callout_calloutlength#(cal#, 10)   ' Shorter pointer

' Right-side annotation
callout_calloutposition#(cal#, 3)  ' Right
ⓘ Note: The pointer extends beyond the bounding box dimensions. When positioning callouts near form edges, account for the extra space the pointer occupies (calloutlength pixels).
ⓘ Note: calloutoffset positions the pointer along the edge measured from the start of that edge (left for top/bottom edges, top for left/right edges).

Rounded Corners

FunctionSignatureDescription
callout_xradius(cal#)callout_xradius@#Get horizontal corner radius
callout_xradius#(cal#, n)callout_xradius#@#nSet horizontal corner radius
callout_yradius(cal#)callout_yradius@#Get vertical corner radius
callout_yradius#(cal#, n)callout_yradius#@#nSet vertical corner radius
callout_corners#(cal#, xr, yr)callout_corners#@#nnSet both corner radii in one call
╯ plan9basic
' Uniform rounded corners
callout_corners#(cal#, 10, 10)

' Pill-shaped (large radius)
callout_corners#(cal#, 20, 20)

' Sharp corners (default)
callout_corners#(cal#, 0, 0)
ⓘ Note: Use equal xradius and yradius for uniform rounding. Different values create elliptical corners. Use callout_corners# as a shortcut to set both at once.

Fill Properties

FunctionSignatureDescription
callout_fill$(cal#)callout_fill$@#Get fill color as hex string
callout_fill#(cal#, color$)callout_fill#@#$Set fill color ("#RRGGBB" or "#AARRGGBB")
callout_fillnone#(cal#)callout_fillnone#@#Remove fill (fully transparent body)
╯ plan9basic
callout_fill#(cal#, "#FFFFFF")       ' White fill
callout_fill#(cal#, "#80FF0000")   ' Semi-transparent red (alpha=80)
callout_fillnone#(cal#)              ' No fill (outline only)
ⓘ Note: Use #AARRGGBB format for semi-transparent fills. The alpha channel (AA) ranges from 00 (transparent) to FF (opaque).

Stroke Properties

FunctionSignatureDescription
callout_stroke$(cal#)callout_stroke$@#Get stroke color as hex string
callout_stroke#(cal#, color$)callout_stroke#@#$Set stroke color
callout_strokenone#(cal#)callout_strokenone#@#Remove stroke (no outline)
callout_strokethickness(cal#)callout_strokethickness@#Get stroke thickness
callout_strokethickness#(cal#, n)callout_strokethickness#@#nSet stroke thickness
callout_strokedash(cal#)callout_strokedash@#Get dash style (0–4)
callout_strokedash#(cal#, n)callout_strokedash#@#nSet dash style
callout_strokecap(cal#)callout_strokecap@#Get cap style (0=Flat, 1=Round)
callout_strokecap#(cal#, n)callout_strokecap#@#nSet cap style
callout_strokejoin(cal#)callout_strokejoin@#Get join style (0=Miter, 1=Round, 2=Bevel)
callout_strokejoin#(cal#, n)callout_strokejoin#@#nSet join style
╯ plan9basic
' Solid black border
callout_stroke#(cal#, "#000000")
callout_strokethickness#(cal#, 2)

' Dashed outline
callout_strokedash#(cal#, 1)      ' Dash style
callout_strokejoin#(cal#, 1)      ' Rounded joins

' No border (clean tooltip look)
callout_strokenone#(cal#)

Position & Size

FunctionSignatureDescription
callout_x(cal#)callout_x@#Get X position
callout_x#(cal#, x)callout_x#@#nSet X position
callout_y(cal#)callout_y@#Get Y position
callout_y#(cal#, y)callout_y#@#nSet Y position
callout_width(cal#)callout_width@#Get width
callout_width#(cal#, w)callout_width#@#nSet width
callout_height(cal#)callout_height@#Get height
callout_height#(cal#, h)callout_height#@#nSet height
callout_bounds#(cal#, x, y, w, h)callout_bounds#@#nnnnSet position and size in one call
callout_move#(cal#, x, y)callout_move#@#nnSet position only
callout_size#(cal#, w, h)callout_size#@#nnSet size only

Visual Properties

FunctionSignatureDescription
callout_visible(cal#)callout_visible@#Get visibility (0/1)
callout_visible#(cal#, n)callout_visible#@#nSet visibility
callout_enabled(cal#)callout_enabled@#Get enabled state (0/1)
callout_enabled#(cal#, n)callout_enabled#@#nSet enabled state
callout_opacity(cal#)callout_opacity@#Get opacity (0.0–1.0)
callout_opacity#(cal#, n)callout_opacity#@#nSet opacity
callout_hittest(cal#)callout_hittest@#Get hit-test state (0/1)
callout_hittest#(cal#, n)callout_hittest#@#nEnable/disable mouse hit testing
callout_rotation(cal#)callout_rotation@#Get rotation angle in degrees
callout_rotation#(cal#, angle)callout_rotation#@#nSet rotation angle
callout_invalidate#(cal#)callout_invalidate#@#Force the callout to redraw
╯ plan9basic
' Tooltip: initially hidden, show on hover
callout_visible#(tip#, 0)

' Semi-transparent callout
callout_opacity#(cal#, 0.85)

' Enable mouse events on the callout
callout_hittest#(cal#, 1)

' Rotate 45 degrees
callout_rotation#(cal#, 45)
ⓘ Note: callout_hittest# must be set to 1 for the callout to receive mouse events. By default, shapes may not respond to clicks.

Alignment & Margins

FunctionSignatureDescription
callout_align(cal#)callout_align@#Get alignment
callout_align#(cal#, n)callout_align#@#nSet alignment
callout_margin#(cal#, n)callout_margin#@#nSet uniform margin on all four sides
callout_margins#(cal#, l, t, r, b)callout_margins#@#nnnnSet individual margins
callout_marginleft(cal#)callout_marginleft@#Get left margin
callout_marginleft#(cal#, n)callout_marginleft#@#nSet left margin
callout_margintop(cal#)callout_margintop@#Get top margin
callout_margintop#(cal#, n)callout_margintop#@#nSet top margin
callout_marginright(cal#)callout_marginright@#Get right margin
callout_marginright#(cal#, n)callout_marginright#@#nSet right margin
callout_marginbottom(cal#)callout_marginbottom@#Get bottom margin
callout_marginbottom#(cal#, n)callout_marginbottom#@#nSet bottom margin

Tag & Parent

FunctionSignatureDescription
callout_tag(cal#)callout_tag@#Get user-defined integer tag
callout_tag#(cal#, n)callout_tag#@#nSet user-defined integer tag
callout_parent#(cal#)callout_parent#@#Get parent control pointer
callout_parent#(cal#, parent#)callout_parent#@##Move callout to a different parent
callout_bringtofront#(cal#)callout_bringtofront#@#Bring to front of Z-order
callout_sendtoback#(cal#)callout_sendtoback#@#Send to back of Z-order

Events

Each event has a setter (callout_onXXX#(cal#, func$)) and a getter (callout_onXXX$(cal#)). Use callout_clearcallbacks#(cal#) to disconnect all callbacks at once.

Click & Resize Events

Event SetterGetterCallback Signature
callout_onclick#(cal#, func$)callout_onclick$(cal#)function name(sender#)
callout_ondblclick#(cal#, func$)callout_ondblclick$(cal#)function name(sender#)
callout_onresize#(cal#, func$)callout_onresize$(cal#)function name(sender#)

Mouse Events

Event SetterGetterCallback Signature
callout_onmousedown#(cal#, func$)callout_onmousedown$(cal#)function name(sender#, button, x, y, shift$)
callout_onmouseup#(cal#, func$)callout_onmouseup$(cal#)function name(sender#, button, x, y, shift$)
callout_onmousemove#(cal#, func$)callout_onmousemove$(cal#)function name(sender#, x, y, shift$)
callout_onmouseenter#(cal#, func$)callout_onmouseenter$(cal#)function name(sender#)
callout_onmouseleave#(cal#, func$)callout_onmouseleave$(cal#)function name(sender#)
callout_onmousewheel#(cal#, func$)callout_onmousewheel$(cal#)function name(sender#, delta, shift$)
callout_clearcallbacks#(cal#)Disconnect all event callbacks
⚠ Warning: Set callout_hittest#(cal#, 1) before assigning event handlers. Without hit testing enabled, the callout won't receive mouse events.

Complete Examples

Speech Bubble

╯ speech.bas
let frm# = form#("Speech Bubble", 400, 300)
form_position#(frm#, 4)

let cal# = callout#(frm#, 50, 30, 250, 80)
callout_fill#(cal#, "#FFFFFF")
callout_stroke#(cal#, "#2c3e50")
callout_strokethickness#(cal#, 2)
callout_corners#(cal#, 10, 10)
callout_calloutposition#(cal#, 2)    ' Bottom pointer
callout_calloutlength#(cal#, 20)
callout_calloutwidth#(cal#, 15)
callout_calloutoffset#(cal#, 30)

let lbl# = label#(cal#, "Hello World!")
label_move#(lbl#, 20, 25)

form_show(frm#)

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

Interactive Tooltip

╯ tooltip.bas
function ShowTip(sender#)
    callout_visible#(tip#, 1)
endfunction

function HideTip(sender#)
    callout_visible#(tip#, 0)
endfunction

let frm# = form#("Tooltip", 400, 300)
form_position#(frm#, 4)

let btn# = button#(frm#, "Hover Me", 150, 150, 100, 40)

' Dark tooltip above the button
let tip# = callout#(frm#, 100, 80, 200, 50)
callout_fill#(tip#, "#34495e")
callout_strokenone#(tip#)
callout_corners#(tip#, 5, 5)
callout_calloutposition#(tip#, 2)    ' Bottom pointer
callout_calloutlength#(tip#, 10)
callout_calloutwidth#(tip#, 12)
callout_calloutoffset#(tip#, 100)
callout_visible#(tip#, 0)             ' Hidden initially

let lblTip# = label#(tip#, "Click to perform action")
label_move#(lblTip#, 15, 15)
label_fontcolor#(lblTip#, "#FFFFFF")

button_onmouseenter#(btn#, "ShowTip")
button_onmouseleave#(btn#, "HideTip")

form_show(frm#)

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

Comic Dialog Bubbles

╯ comic.bas
let frm# = form#("Comic Dialog", 500, 400)
form_position#(frm#, 4)

' First character - pointer at bottom
let bubble1# = callout#(frm#, 50, 30, 200, 80)
callout_fill#(bubble1#, "#FFFFFF")
callout_stroke#(bubble1#, "#000000")
callout_strokethickness#(bubble1#, 2)
callout_corners#(bubble1#, 20, 20)
callout_calloutposition#(bubble1#, 2)
callout_calloutlength#(bubble1#, 25)
callout_calloutoffset#(bubble1#, 40)
let lbl1# = label#(bubble1#, "Hi there!")
label_move#(lbl1#, 60, 30)

' Second character - pointer at top
let bubble2# = callout#(frm#, 250, 180, 200, 80)
callout_fill#(bubble2#, "#FFFFFF")
callout_stroke#(bubble2#, "#000000")
callout_strokethickness#(bubble2#, 2)
callout_corners#(bubble2#, 20, 20)
callout_calloutposition#(bubble2#, 0)
callout_calloutlength#(bubble2#, 25)
callout_calloutoffset#(bubble2#, 150)
let lbl2# = label#(bubble2#, "Hello!")
label_move#(lbl2#, 70, 30)

form_show(frm#)

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

Annotation Callout

╯ annotation.bas
let frm# = form#("Annotation", 400, 350)
form_position#(frm#, 4)

' Content area
let rect# = rectangle#(frm#, 50, 100, 300, 200)
rectangle_fill#(rect#, "#ecf0f1")
rectangle_stroke#(rect#, "#bdc3c7")

' Annotation pointing left toward content
let ann# = callout#(frm#, 250, 20, 130, 60)
callout_fill#(ann#, "#f1c40f")
callout_stroke#(ann#, "#f39c12")
callout_strokethickness#(ann#, 2)
callout_corners#(ann#, 5, 5)
callout_calloutposition#(ann#, 1)    ' Left pointer
callout_calloutlength#(ann#, 15)
callout_calloutoffset#(ann#, 30)

let lbl# = label#(ann#, "Note!")
label_move#(lbl#, 45, 18)
label_fontsize#(lbl#, 14)
label_bold#(lbl#, 1)

form_show(frm#)

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

Best Practices

PracticeWhy
Account for pointer length when positioningThe pointer extends beyond the bounding box by calloutlength pixels
Use callout_corners# for rounded speech bubblesRounded corners (10–20px) give a more natural speech bubble appearance
Set hittest = 1 for interactive calloutsRequired for mouse events to fire
Use callout_visible# for tooltip show/hideToggle visibility with mouse enter/leave on the target control
Place labels inside callouts for textCallouts are shapes, not text controls — add a child Label for content
Use callout_strokenone# for clean tooltipsBorderless dark-background callouts look modern
Use calloutoffset to aim the pointerPosition the pointer to point at the specific element being annotated
Use #AARRGGBB for semi-transparent fillsAlpha channel enables overlay effects without fully obscuring content
⚠ Warning: The callout pointer extends outside the bounding rectangle. If the callout is near a form edge, the pointer may be clipped. Position the callout far enough from edges to accommodate the calloutlength.

Quick Reference

FunctionSignatureDescription
callout_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
callout#(parent#[, w, h] | [, x, y, w, h])variousCreate (3 overloads)
callout_free(cal#)callout_free@#Destroy
callout_calloutposition / calloutlength / calloutwidth / calloutoffsetvariousCallout properties (8)
callout_xradius / yradius / corners#variousRounded corners (6)
callout_fill$ / fill# / fillnone#variousFill (3)
callout_stroke$ / stroke# / strokenone# / strokethickness / strokedash / strokecap / strokejoinvariousStroke (11)
callout_x/y/width/height (get/set) / bounds# / move# / size#variousPosition & size (14)
callout_visible / enabled / opacity / hittest / rotation / invalidate#variousVisual properties (8)
callout_align / margin# / margins# / margin[left/top/right/bottom]variousAlignment & margins (12)
callout_tag / parent# / bringtofront# / sendtoback#variousTag & parent (6)
callout_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onresizevariousEvents set+get (17)
callout_clearcallbacks#callout_clearcallbacks#@#Disconnect all events

94 functions. Part of the Plan9Basic GUI shape library system.