Brodgar client

hafen.ui: The Client’s Own Controls

A control is one of the client’s interactive widgets, the class its own windows are built from. Your addon builds, configures and owns it. The client draws it, handles the press and dresses it from the stylesheet.

local harvest_window = hafen.ui():window():title("Harvest"):size(160, 60):position(80, 120)

local go_button = hafen.ui():button():parent(harvest_window):position(20, 20):text("Go")
go_button:on("Pressed", function() hafen.log():write("pressed") end)

A control is a Widget

A builder hands back the same Widget every lookup gives you. Every read and write on that page answers on a control: :type(), :role(), :position(x, y), :size(w, h), :parent(w), :visible(b), :destroy(), :style(), :rule(), :info(). :type() reports the client’s class, so a button you built and one you found both read "Button". One selector, one role and one stylesheet key name both.

Builders

Method Returns Permission The control Page
hafen.ui():button() Widget Unprotected A push button showing a caption or a picture. interactive
hafen.ui():entry() Widget Unprotected A single-line text field. interactive
hafen.ui():label() Widget Unprotected A line of text. display
hafen.ui():image() Widget Unprotected A static picture. display
hafen.ui():separator() Widget Unprotected A horizontal rule. display
hafen.ui():progress() Widget Unprotected A fill-fraction bar. display
hafen.ui():check() Widget Unprotected A checkbox showing a caption or a picture. interactive
hafen.ui():radio() Widget Unprotected Buttons of which exactly one is checked. interactive
hafen.ui():slider() Widget Unprotected A draggable position within a range. interactive
hafen.ui():scroll() Widget Unprotected A scrolling container for other controls. interactive
hafen.ui():scrollbar() Widget Unprotected A bare scroll thumb, for driving something yourself. interactive
hafen.ui():keybinding() Widget Unprotected The client’s key button, joined to one of your hotkeys, or under client.settings to any binding. interactive
hafen.ui():listbox(), :dropdown(), :menu(), :grid(), :table() Widget Unprotected The row-source controls. lists
hafen.ui():column(), :row() Widget Unprotected Not controls: surfaces of yours that lay the controls in them out. column

No builder takes an argument. A control is built bare with the client’s defaults and configured by chained setters. It draws nothing until the tick after the statement that built it, so it is never seen half-built. :parent(w) chooses where it hangs while it is being built. On screen it moves by :position(x, y).

Setters

Method Returns Permission Description
:text(caption) / :text() self / string \| nil Unprotected The caption the control displays. The read answers on any text-bearing widget. On a client control the write is a restoring level.
:image(up, down [, hover]) / :image() self / table \| nil Unprotected The pictures the control shows instead of a caption.
:value(v) / :value() self / any \| nil Unprotected on a control you built. widget.value on a client control What the control holds.
:source(h) / :source() self / string \| userdata \| nil Unprotected The picture a picture control shows.
:rows(t) / :rows() self / table \| nil Unprotected The row source a radio or a row-source control takes.
:range(min, max) / :range() self / {min=, max=} \| nil Unprotected The bounds of a slider or scrollbar.
:bind(opt), :bind(binding) / :bind() self / Option \| Binding \| nil Unprotected The option of your addon’s a checkbox, slider, dropdown, radio or entry shows and writes, or the hotkey a key button assigns: yours, or under client.settings another addon’s or the client’s. :bind(nil) unbinds.

Every setter returns the Widget and has a bare read. None is protected on a control you built: it is your own client-side UI, restored with your addon.

Rule Detail
:bind(opt) The control takes the option’s value at once and is configured from it. A slider’s :range comes from its bounds, a dropdown’s or radio’s :rows from its choices. The user moving the control writes opt:value(v). A write to the option moves the control without firing its Changed. A control of another kind than the option’s is refused naming the one it takes. So is a control holding no value, and a client control.
:bind(binding) A key button’s alone: a Binding of a hotkey your addon declared and has not ended, its key shown at once. Under client.settings, another addon’s declared hotkey or one of the client’s bindings too. An Option given to a key button, and a Binding given to any other control, are refused naming the control that takes it.
:value() One verb for whatever a control holds: a progress bar’s fraction, a checkbox’s boolean, an entry’s string, a key button’s key. nil where it holds nothing. On a client control the write is driving and protected. A key button refuses the write, yours or the client’s: its key is its binding’s.
Value checks The type first: a number is a number and "50" is a string. :range(min, max)’s bounds are read the same way. A progress bar refuses a value outside 0..1, naming the rule. A slider or scrollbar clamps a value outside its :range to the nearer bound, that range being yours to narrow at any time.

Sizing

Method Returns Permission Description
:size(w) self Unprotected Sets the width. The height is the control’s own art. Answered by the button, key button, text entry, checkbox, dropdown, slider and separator.
:size(w, h) self Unprotected Sets both, in design pixels. A box under the art’s own raises, naming the height it needs: widget:size(w, h) — a Button is 24 design px tall, which is its own ART's box: 4 clips it.
local go_button = hafen.ui():button():size(80):text("Go")
go_button:size()                     -- {w = 80, h = 24}, at any interface scale
Rule Detail
A control showing a picture A button or checkbox given :image(...) is that picture in both directions and takes no :size.
Stretchable controls A listbox, grid, table or picture has no art to ask: it takes :size(w, h) and refuses :size(w), as a surface does.
Clipping A caption wider than its box is drawn clipped, not wrapped.
Containers A column places controls one under the other at their own size. :pack() sizes a window around them and keeps it sized.

Subscribing

A control answers the universal :on(key, fn) keys plus one capability key, the thing that control does.

Builder Key Handler receives
:button() Pressed —
:check(), :radio(), :slider(), :scrollbar(), :scroll(), :listbox(), :dropdown() Changed The value — interactive, lists.
:keybinding() Changed The key it now shows, nil for unbound — interactive.
:entry() Changed, Submitted The text.
:menu() Selected The picked row.
:grid() Cell event — grid.
:label(), :image(), :separator(), :progress(), :table() none —
Rule Detail
Pressed is an activation What the button did, from the mouse or the keyboard. No coordinates. The handler may destroy the window the button sits in.
Changed fires on a real interaction only A :value(v) write of yours never re-enters it. Two handlers on one key both fire, in registration order.
The key belongs to the control, not to who built it The same key answers on a client control, with an event that can stop or run the client’s action — edit. On yours the handler is the action: nothing to cancel, only the value handed over. A borrowed slider’s or scrollbar’s Changed is cancelable on neither: the value is written before it is reported.
Inner client widgets A dropdown’s arrow is one of the client’s checkboxes with a Changed of its own. Your Changed on the dropdown is the picked row and never the arrow’s or the popup list’s.

What a control does not take

Draw, Update, Drop, Close and :font(h) belong to a surface you paint. A control refuses them, naming :on("Pressed", fn) and the stylesheet. The universal input keys (MouseDown, MouseUp, MouseMove, Wheel, Removed) it answers like any widget.

Owned and borrowed

A control your addon built is owned: the setters answer, :destroy() ends it, :reload and disable remove it. A client control is borrowed. The reads answer. Every setter refuses naming what to do instead (:info().owned tells which you hold). Its capability key answers on both: subscribing to a borrowed control is how you take over what it does. A control found again with hafen.ui():hit(x, y) or a selector is the same object the builder returned.


See Also