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.
| Category | Count | Description |
|---|---|---|
| Error Handling | 4 | callout_error, errormsg$, strerror$, clearerror |
| Creation & Destruction | 4 | callout# (3 overloads), callout_free |
| Callout Properties | 8 | calloutposition, calloutlength, calloutwidth, calloutoffset (get/set) |
| Rounded Corners | 6 | xradius, yradius (get/set), corners# |
| Fill | 3 | fill$ (get), fill# (set), fillnone# |
| Stroke | 11 | stroke$ / stroke# / strokenone#, thickness, dash, cap, join (get/set) |
| Position & Size | 14 | x, y, width, height (get/set), bounds#, move#, size# |
| Visual Properties | 8 | visible, enabled, opacity, hittest, rotation (get/set), invalidate# |
| Alignment & Margins | 12 | align (get/set), margin#, margins#, marginleft/top/right/bottom (get/set) |
| Tag & Parent | 6 | tag (get/set), parent# (get/set), bringtofront#, sendtoback# |
| Events | 18 | 8 event types × set/get + onmousewheel + clearcallbacks# |
Cross-Platform Support
| Platform | Status | Notes |
|---|---|---|
| Windows | ✅ Full Support | Win32/Win64 |
| Linux | ✅ Full Support | GTK-based |
| Android | ✅ Full Support | Touch-friendly |
Error Handling
| Function | Signature | Description |
|---|---|---|
callout_error() | callout_error@ | Last error code (0 = no error) |
callout_errormsg$() | callout_errormsg$@ | Last error message as string |
callout_strerror$(code) | callout_strerror$@n | Description for a given error code |
callout_clearerror() | callout_clearerror@ | Clear the error state |
Numeric Values Reference
Callout Position callout_calloutposition#
| Value | Position | Pointer Points | Use Case |
|---|---|---|---|
| 0 | Top | Upward from top edge | Tooltip below a control |
| 1 | Left | Left from left edge | Annotation to the right |
| 2 | Bottom | Downward from bottom edge | Speech bubble above a character |
| 3 | Right | Right from right edge | Annotation to the left |
Stroke Dash Style callout_strokedash#
| Value | Style |
|---|---|
| 0 | Solid |
| 1 | Dash |
| 2 | Dot |
| 3 | DashDot |
| 4 | DashDotDot |
Stroke Cap Style callout_strokecap#
| Value | Style |
|---|---|
| 0 | Flat |
| 1 | Round |
Stroke Join Style callout_strokejoin#
| Value | Style |
|---|---|
| 0 | Miter (sharp corners) |
| 1 | Round |
| 2 | Bevel (cut corners) |
Control Alignment callout_align#
| Value | Description |
|---|---|
| 0 | None (absolute positioning) |
| 1 | Top |
| 2 | Left |
| 3 | Right |
| 4 | Bottom |
| 9 | Client (fill parent) |
| 11 | Center |
Creation & Destruction
| Function | Signature | Description |
|---|---|---|
callout#(parent#) | callout#@# | Create callout with default size |
callout#(parent#, w, h) | callout#@#nn | Create with specified size |
callout#(parent#, x, y, w, h) | callout#@#nnnn | Create with position and size |
callout_free(cal#) | callout_free@# | Destroy callout shape |
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.
| Function | Signature | Description |
|---|---|---|
callout_calloutposition(cal#) | callout_calloutposition@# | Get pointer side (0=Top, 1=Left, 2=Bottom, 3=Right) |
callout_calloutposition#(cal#, n) | callout_calloutposition#@#n | Set which side the pointer extends from |
callout_calloutlength(cal#) | callout_calloutlength@# | Get pointer length (how far it extends) |
callout_calloutlength#(cal#, n) | callout_calloutlength#@#n | Set pointer length |
callout_calloutwidth(cal#) | callout_calloutwidth@# | Get pointer base width |
callout_calloutwidth#(cal#, n) | callout_calloutwidth#@#n | Set pointer base width |
callout_calloutoffset(cal#) | callout_calloutoffset@# | Get pointer position along the edge |
callout_calloutoffset#(cal#, n) | callout_calloutoffset#@#n | Set pointer offset along the edge |
' 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
| Function | Signature | Description |
|---|---|---|
callout_xradius(cal#) | callout_xradius@# | Get horizontal corner radius |
callout_xradius#(cal#, n) | callout_xradius#@#n | Set horizontal corner radius |
callout_yradius(cal#) | callout_yradius@# | Get vertical corner radius |
callout_yradius#(cal#, n) | callout_yradius#@#n | Set vertical corner radius |
callout_corners#(cal#, xr, yr) | callout_corners#@#nn | Set both corner radii in one call |
' 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
| Function | Signature | Description |
|---|---|---|
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) |
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
| Function | Signature | Description |
|---|---|---|
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#@#n | Set stroke thickness |
callout_strokedash(cal#) | callout_strokedash@# | Get dash style (0–4) |
callout_strokedash#(cal#, n) | callout_strokedash#@#n | Set dash style |
callout_strokecap(cal#) | callout_strokecap@# | Get cap style (0=Flat, 1=Round) |
callout_strokecap#(cal#, n) | callout_strokecap#@#n | Set cap style |
callout_strokejoin(cal#) | callout_strokejoin@# | Get join style (0=Miter, 1=Round, 2=Bevel) |
callout_strokejoin#(cal#, n) | callout_strokejoin#@#n | Set join style |
' 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
| Function | Signature | Description |
|---|---|---|
callout_x(cal#) | callout_x@# | Get X position |
callout_x#(cal#, x) | callout_x#@#n | Set X position |
callout_y(cal#) | callout_y@# | Get Y position |
callout_y#(cal#, y) | callout_y#@#n | Set Y position |
callout_width(cal#) | callout_width@# | Get width |
callout_width#(cal#, w) | callout_width#@#n | Set width |
callout_height(cal#) | callout_height@# | Get height |
callout_height#(cal#, h) | callout_height#@#n | Set height |
callout_bounds#(cal#, x, y, w, h) | callout_bounds#@#nnnn | Set position and size in one call |
callout_move#(cal#, x, y) | callout_move#@#nn | Set position only |
callout_size#(cal#, w, h) | callout_size#@#nn | Set size only |
Visual Properties
| Function | Signature | Description |
|---|---|---|
callout_visible(cal#) | callout_visible@# | Get visibility (0/1) |
callout_visible#(cal#, n) | callout_visible#@#n | Set visibility |
callout_enabled(cal#) | callout_enabled@# | Get enabled state (0/1) |
callout_enabled#(cal#, n) | callout_enabled#@#n | Set enabled state |
callout_opacity(cal#) | callout_opacity@# | Get opacity (0.0–1.0) |
callout_opacity#(cal#, n) | callout_opacity#@#n | Set opacity |
callout_hittest(cal#) | callout_hittest@# | Get hit-test state (0/1) |
callout_hittest#(cal#, n) | callout_hittest#@#n | Enable/disable mouse hit testing |
callout_rotation(cal#) | callout_rotation@# | Get rotation angle in degrees |
callout_rotation#(cal#, angle) | callout_rotation#@#n | Set rotation angle |
callout_invalidate#(cal#) | callout_invalidate#@# | Force the callout to redraw |
' 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
| Function | Signature | Description |
|---|---|---|
callout_align(cal#) | callout_align@# | Get alignment |
callout_align#(cal#, n) | callout_align#@#n | Set alignment |
callout_margin#(cal#, n) | callout_margin#@#n | Set uniform margin on all four sides |
callout_margins#(cal#, l, t, r, b) | callout_margins#@#nnnn | Set individual margins |
callout_marginleft(cal#) | callout_marginleft@# | Get left margin |
callout_marginleft#(cal#, n) | callout_marginleft#@#n | Set left margin |
callout_margintop(cal#) | callout_margintop@# | Get top margin |
callout_margintop#(cal#, n) | callout_margintop#@#n | Set top margin |
callout_marginright(cal#) | callout_marginright@# | Get right margin |
callout_marginright#(cal#, n) | callout_marginright#@#n | Set right margin |
callout_marginbottom(cal#) | callout_marginbottom@# | Get bottom margin |
callout_marginbottom#(cal#, n) | callout_marginbottom#@#n | Set bottom margin |
Tag & Parent
| Function | Signature | Description |
|---|---|---|
callout_tag(cal#) | callout_tag@# | Get user-defined integer tag |
callout_tag#(cal#, n) | callout_tag#@#n | Set 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 Setter | Getter | Callback 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 Setter | Getter | Callback 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
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
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
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
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
| Practice | Why |
|---|---|
| Account for pointer length when positioning | The pointer extends beyond the bounding box by calloutlength pixels |
Use callout_corners# for rounded speech bubbles | Rounded corners (10–20px) give a more natural speech bubble appearance |
Set hittest = 1 for interactive callouts | Required for mouse events to fire |
Use callout_visible# for tooltip show/hide | Toggle visibility with mouse enter/leave on the target control |
| Place labels inside callouts for text | Callouts are shapes, not text controls — add a child Label for content |
Use callout_strokenone# for clean tooltips | Borderless dark-background callouts look modern |
Use calloutoffset to aim the pointer | Position the pointer to point at the specific element being annotated |
Use #AARRGGBB for semi-transparent fills | Alpha 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
| Function | Signature | Description |
|---|---|---|
callout_error / errormsg$ / strerror$ / clearerror | various | Error handling (4) |
callout#(parent#[, w, h] | [, x, y, w, h]) | various | Create (3 overloads) |
callout_free(cal#) | callout_free@# | Destroy |
callout_calloutposition / calloutlength / calloutwidth / calloutoffset | various | Callout properties (8) |
callout_xradius / yradius / corners# | various | Rounded corners (6) |
callout_fill$ / fill# / fillnone# | various | Fill (3) |
callout_stroke$ / stroke# / strokenone# / strokethickness / strokedash / strokecap / strokejoin | various | Stroke (11) |
callout_x/y/width/height (get/set) / bounds# / move# / size# | various | Position & size (14) |
callout_visible / enabled / opacity / hittest / rotation / invalidate# | various | Visual properties (8) |
callout_align / margin# / margins# / margin[left/top/right/bottom] | various | Alignment & margins (12) |
callout_tag / parent# / bringtofront# / sendtoback# | various | Tag & parent (6) |
callout_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onresize | various | Events set+get (17) |
callout_clearcallbacks# | callout_clearcallbacks#@# | Disconnect all events |
94 functions. Part of the Plan9Basic GUI shape library system.