D/Works widgets¶
Buttons are easy. Agreeing on focus, damage, keyboard activation, menus, resizing, and many renderers is the hard part.
D/Works is the standard widget source library written in D/BASIC. It gives applications a common visual and interaction vocabulary without hiding the application behind a framework.
Why widgets belong in the language story¶
Without a shared library, every application invents a slightly different button, caret, focus ring, menu, hit test, and redraw rule. Those differences multiply across a browser, three desktop operating systems, and D/OS heads with very different display capabilities.
D/Works centralizes the reusable part:
- theme colors and measurements;
- bounded widget geometry and state;
- consistent focus, pressed, checked, and disabled visuals;
- editable text with a visible caret;
- local typed control events;
- explicit damage tracking; and
- popup/menu behavior with known bounds.
It still leaves program meaning where it belongs—in your application.
Import it¶
The compiler assembles the library at build time or binds its exact shared artifact, then emits the same target-blind DBC vocabulary as the rest of the program. There is no browser widget rewrite, Mac-only control set, or hidden JavaScript event model.
Who owns what¶
| Owner | Responsibilities |
|---|---|
| application | model, lifecycle, layout, focus order, pointer routing, command meaning, optional modal policy |
| widget | reusable control state, visual behavior, local event reduction, damage marking, GFX painting |
| runner | normalized input delivery, capability admission, font/color/render route, frame presentation |
This division is the secret. Widgets are ordinary objects. They do not install callbacks or take over the event loop.
The catalog¶
Policy and local coordination¶
| Type | Role |
|---|---|
WorksTheme |
canonical RGB565 palette and visual roles |
WorksMetrics |
margins, gaps, control sizes, and layout measurements |
WorksSurfaceDamage |
surface-owned dirty/expose ledger |
WorksLocalEvent |
synchronous action value: source, kind, integer value, and note |
Base and presentation¶
| Type | Role |
|---|---|
WorksWidget |
base geometry, visibility, enablement, focus, damage, clipping, and paint contract |
WorksPanel |
bounded panel with optional caption |
WorksLabel |
static text label |
Compact controls¶
| Type | Role |
|---|---|
WorksButton |
labelled command button |
WorksTextField |
single-line ASCII editor with selection-free caret navigation |
WorksToggle |
check/toggle control |
WorksIconButton |
compact glyph command with optional label and mark |
WorksTooltip |
application-positioned bounded hint |
Commands and transient surfaces¶
| Type | Role |
|---|---|
WorksPopoverFrame |
attached bounded popup frame |
WorksCommandRow |
row-shaped icon/text command |
WorksMenuItem |
one stable menu command, check, separator, disabled, and child-pane state |
WorksMenu |
app-owned anchored menu with up to eight physical item slots |
WorksMenuCascade |
allocation-free cascade of up to four panes |
WorksStatusStrip |
neutral, working, warning, and ready feedback |
The setup recipe¶
Every control follows the same readable sequence:
DIM Theme AS WorksTheme
DIM Damage AS WorksSurfaceDamage
DIM Save AS WorksButton
Theme = NEW WorksTheme()
Damage = NEW WorksSurfaceDamage()
Save = NEW WorksButton("save", "Save")
Save.Attach Damage
Save.ApplyTheme Theme
Save.SetBounds 12, 12, 80, 24
Save.SetFocused -1
- construct the shared theme and surface damage ledger;
- construct a widget with stable identity and visible caption;
- attach the widget to the surface ledger;
- apply the theme;
- assign bounds from application layout; and
- set visible interaction state through methods.
Stable identity such as "save" is application vocabulary. A glyph or translated caption is presentation.
A complete one-button window¶
IMPORT dworks.widgets BAKED
DIM SHARED Theme AS WorksTheme
DIM SHARED Damage AS WorksSurfaceDamage
DIM SHARED Event AS WorksLocalEvent
DIM SHARED Save AS WorksButton
DIM SHARED Width AS INTEGER
DIM SHARED Height AS INTEGER
DIM SHARED PreviousButtons AS INTEGER
DIM SHARED Pressed AS INTEGER
WINDOW SUB OPEN()
Width = 320
Height = 192
Theme = NEW WorksTheme()
Damage = NEW WorksSurfaceDamage()
Event = NEW WorksLocalEvent()
Save = NEW WorksButton("save", "Save")
Save.Attach Damage
Save.ApplyTheme Theme
Save.SetBounds 12, 12, 80, 24
Save.SetFocused -1
Damage.RequestExpose
SERVICE SET_TITLE("Widget hello")
SERVICE INVALIDATE()
END SUB
WINDOW SUB DRAW()
INTENT GFX.BEGIN_FRAME()
INTENT GFX.FILL_RECT(0, 0, Width, Height, Theme.Canvas)
Save.Paint
INTENT GFX.PRESENT()
Damage.Painted
END SUB
WINDOW SUB RESIZE(W AS INTEGER, H AS INTEGER)
Width = W
Height = H
Save.SetBounds 12, 12, 80, 24
Damage.RequestExpose
SERVICE INVALIDATE()
END SUB
WINDOW SUB CLOSE()
Save = NOTHING
Event = NOTHING
Damage = NOTHING
Theme = NOTHING
END SUB
ON KEY SUB (K AS INTEGER)
IF K = 13 THEN
Save.Activate Event
SERVICE SET_TITLE("Activated: " + Event.Note)
END IF
IF Damage.Pending() THEN SERVICE INVALIDATE()
END SUB
ON POINTER SUB (X AS INTEGER, Y AS INTEGER, Buttons AS INTEGER)
IF Buttons <> 0 AND PreviousButtons = 0 THEN
Pressed = X >= Save.X AND X < Save.X + Save.Width AND Y >= Save.Y AND Y < Save.Y + Save.Height
Save.SetDown Pressed
END IF
IF Buttons = 0 AND PreviousButtons <> 0 THEN
IF Pressed AND X >= Save.X AND X < Save.X + Save.Width AND Y >= Save.Y AND Y < Save.Y + Save.Height THEN
Save.Activate Event
SERVICE SET_TITLE("Activated: " + Event.Note)
END IF
Save.SetDown 0
Pressed = 0
END IF
PreviousButtons = Buttons
IF Damage.Pending() THEN SERVICE INVALIDATE()
END SUB
The example is deliberately explicit. The host delivers input; the application decides which bounds received it; the widget reduces its own activation and visual state; the application gives Event.Source and Event.Kind meaning.
Try the larger D/Works widget form in the playground. It adds a text field, toggle, focus traversal, click-to-place caret, resizing, and a controller class.
Local events are values, not callbacks¶
Save.Activate Event
IF Event.Source = "save" AND Event.Kind = "activate" THEN
CALL SaveDocument
END IF
WorksLocalEvent contains:
| Field | Meaning |
|---|---|
Source |
stable widget/command identity |
Kind |
action category such as activation, toggle, text, or menu |
Value |
small typed integer payload |
Note |
text payload or human detail |
The event is synchronous and application-owned. It is not a DOBJ transport, OS message, or deferred host callback. The receiving controller can inspect it, update the model, and clear or reuse it in the same turn.
Focus is a route you own¶
For a three-control form, a small integer is enough:
SUB Form.SetFocus (NextFocus AS INTEGER)
THIS.Focus = NextFocus
THIS.Title.SetFocused THIS.Focus = 1
THIS.Wrap.SetFocused THIS.Focus = 2
THIS.Save.SetFocused THIS.Focus = 3
END SUB
SUB Form.AdvanceFocus ()
THIS.Focus = THIS.Focus + 1
IF THIS.Focus > 3 THEN THIS.Focus = 1
THIS.SetFocus THIS.Focus
END SUB
Handle Tab in ON KEY, and route other keys to the focused control. This makes focus order obvious, testable, and responsive to compact layouts.
Text fields and the caret¶
WorksTextField provides:
AcceptKey(K)for printable ASCII, Backspace, Delete, Left/Right, and Home/End;PlaceCaret(PX)for pointer-based placement;RevealCaret()to keep the insertion point inside the visible field;PublishText(Event)to reduce the current edit; andSetText(Value)for model-driven replacement.
The caret belongs to widget state. The runner may render its font with a high-density browser or desktop face, but cell metrics and click-to-position behavior remain tied to the semantic text role.
Damage: repaint because something changed¶
Widget setters compare old and new visible state. A real change marks the widget and its attached WorksSurfaceDamage; assigning the same value does not.
During drawing, paint the damaged controls and commit the ledger:
Call RequestExpose when the host exposes or resizes the surface and a complete repair is needed. Damage is correctness first and performance second: stale pixels are wrong, while needless whole-surface repainting makes constrained heads feel slow.
Pointer reduction¶
The application currently compares public X, Y, Width, and Height fields. That is not a leak of widget internals; bounds are the shared controller contract.
Track press and release separately. A press inside followed by a release outside should normally cancel a button activation. Sliders and drags may use a different reducer, but the ownership remains explicit.
Menus are application surfaces¶
WorksMenu is an anchored popup owned by the application, not a global OS menu. Each of its up to eight item slots can carry:
- a visible label and optional glyph;
- a stable
Commandidentity; - an integer
Value; - checked and disabled state;
- a separator; and
- an optional child-pane index.
WorksMenuCascade keeps four physical panes and a separate active path. It opens right when space permits, left when necessary, gives the deepest visible pane pointer precedence, and rejects cycles. Keyboard navigation wraps and skips disabled/separator rows.
An item's glyph is never its command identity. Changing artwork must not change behavior.
This complete console sample builds the model used by a File → Export → PDF/Text cascade:
IMPORT dworks.widgets BAKED
DIM FileMenu AS WorksMenu, ExportMenu AS WorksMenu
DIM SpareTwo AS WorksMenu, SpareThree AS WorksMenu
DIM Menus AS WorksMenuCascade
DIM OpenItem AS WorksMenuItem, ExportItem AS WorksMenuItem
DIM PdfItem AS WorksMenuItem, TextItem AS WorksMenuItem
FileMenu = NEW WorksMenu("file")
OpenItem = NEW WorksMenuItem("open", "file.open", 0, "Open", "Open a document", 0)
ExportItem = NEW WorksMenuItem("export", "file.export", 0, "Export", "Choose a format", 0)
ExportItem.ChildPane = 1
FileMenu.Item0 = OpenItem
FileMenu.Item1 = ExportItem
FileMenu.ItemCount = 2
ExportMenu = NEW WorksMenu("export")
PdfItem = NEW WorksMenuItem("pdf", "export.pdf", 0, "PDF", "Portable document", 1)
TextItem = NEW WorksMenuItem("text", "export.text", 0, "Plain text", "UTF-8 text", 2)
ExportMenu.Item0 = PdfItem
ExportMenu.Item1 = TextItem
ExportMenu.ItemCount = 2
SpareTwo = NEW WorksMenu("spare.two")
SpareThree = NEW WorksMenu("spare.three")
Menus = NEW WorksMenuCascade(FileMenu, ExportMenu, SpareTwo, SpareThree)
PRINT "MENU MODEL READY: "; FileMenu.ItemCount; " + "; ExportMenu.ItemCount
END
In a window controller:
- call
Menus.OpenRoot(...)from the menu button or shortcut; - paint
Menus.Paintafter ordinary content so every open pane is on top; - while
Menus.IsModalOwner()is true, route pointer press/release toMenus.PressandMenus.Releasebefore the underlying controls; - use
Menus.MoveFocus(-1)orMenus.MoveFocus(1)for Up/Down andMenus.ActivateFocused(...)for Enter or Right; Escape callsMenus.Dismiss; - reduce the resulting
WorksLocalEvent.Sourcesuch asexport.pdfthrough the application's normal command dispatcher; and - request a full surface expose after dismissal, because the pixels underneath the popup now belong to the underlying widgets again.
Menu text roles¶
Choose an exported semantic font constant before opening:
| Constant | Intended role |
|---|---|
WORKS_MENU_FONT_UI |
standard UI text |
WORKS_MENU_FONT_UI_SMALL |
compact UI text |
WORKS_MENU_FONT_NATIVE_MONO |
native monospace/cell face |
WORKS_MENU_FONT_UI_CHROME |
small chrome text |
The runner maps those roles to its best honest face. On a browser or desktop that can mean a real scalable font; on a compact head it can mean a tuned resident face. Rows grow to the resolved line height, so text is not forced through a bitmap-era box.
Theme once, adapt locally¶
WorksTheme carries semantic colors such as canvas, panel, paper, accent, text, and disabled state. WorksMetrics carries shared measurements. Apply them instead of sprinkling literal colors and heights through widget code.
The runner can map canonical color and font roles upward or downward for the destination head. Your layout can adapt in RESIZE; your application does not need IF WINDOWS or IF ATARI branches.
A widget checklist¶
- Give every actionable control a stable identity.
- Apply one theme and consistent metrics.
- Attach visible controls to the surface damage ledger.
- Keep focus order in the application.
- Reduce press/move/release, not isolated clicks.
- Use setters for visible state changes.
- Clip text and transient surfaces.
- Let
WorksLocalEventcross the control/controller boundary. - Let a DATA/Service Intent—not the widget—own persistence.
- Release application-owned object references in
CLOSE.
Next: Resources, Images & Sound →
See also: Windows & Events · Intents & Services · Drag, Drop & DOBJ · Records & Objects · Playground Guide