///

Overlay Widgets

Ui.Overlay.setText/setPanel are the quick form. For buttons, draggable panels, images, progress rings or charts, create a widget and keep its handle. Every setter returns the widget, so calls chain.

var hp = Ui.Overlay.text("hp") .setText("HP: 100%") .at(40, "10%") // px number, "24dp", "10%" .anchor("top-left") .textStyle({ size: 18, color: Color.green(), weight: 700, family: "mono" }) .background(Color.fromHex("#CC101317")) .corner("12dp") .padding(["8dp", "14dp"]) .show(); hp.setText("HP: 42%").setTextColor(Color.red()); // update in place

Constructors

Each takes an optional id (auto-generated if omitted) and an optional property object. Passing the same id again returns a handle to the existing widget.

Keep the handle a constructor returns. Ui.Overlay.get(id) is slower because it checks that the id exists, so avoid it inside loops. For a widget you know exists, Ui.Overlay.ref(id) returns a handle without the check.

ConstructorRenders
Ui.Overlay.text(id?, props?)A text label.
Ui.Overlay.panel(id?, props?)A filled box.
Ui.Overlay.image(id?, props?)An image from your saved templates, a file path, or base64.
Ui.Overlay.button(id?, props?)A tappable label; clickable by default.
Ui.Overlay.progress(id?, props?)A progress bar, horizontal or vertical.
Ui.Overlay.gauge(id?, props?)An arc gauge.
Ui.Overlay.sparkline(id?, props?)An auto-scaled line chart over a rolling sample window.
Ui.Overlay.group(id?, props?)A container; children call .parent(groupId) and position relative to it.

Units and placement

A plain number is pixels. Strings carry a unit: "24dp" scales with screen density, "25%" is a fraction of the parent, "auto" sizes to content.

.anchor() accepts top-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right; the x/y you set become offsets from that anchor. .safeArea(true) keeps the widget clear of the notch and navigation bar.

For layouts that look the same on every device, use dp, % or anchors. To keep pixel coordinates but scale them like the rest of your macro, call Ui.Overlay.setCoordinateMode("scaled") once at the top of your script. The default is "raw".

Setters

.set({ ... })

Change several properties in one call. Faster than chained setters inside an update loop.

let result = .set('demo'); console.log(result);

.at(x, y)

Position.

let result = .at(500, 1200); console.log(result);

.setX(x)

Position.

.setX(500);

.setY(y)

Position.

.setY(1200);

.size(w, h)

Size; "auto" wraps content.

let result = .size(300, 120); console.log(result);

.setW(w)

Size; "auto" wraps content.

.setW(300);

.setH(h)

Size; "auto" wraps content.

.setH(120);

.anchor(a)

Placement. padding/margin take one value, [vertical, horizontal], or [l, t, r, b].

let result = .anchor(255); console.log(result);

.padding(v)

Placement. padding/margin take one value, [vertical, horizontal], or [l, t, r, b].

let result = .padding('demo'); console.log(result);

.margin(v)

Placement. padding/margin take one value, [vertical, horizontal], or [l, t, r, b].

let result = .margin('demo'); console.log(result);

.safeArea(b)

Placement. padding/margin take one value, [vertical, horizontal], or [l, t, r, b].

let result = .safeArea(255); console.log(result);

.parent(groupId)

Grouping and stacking order.

let result = .parent('demo'); console.log(result);

.z(index)

Grouping and stacking order.

let result = .z('demo'); console.log(result);

.background(color)

Fill. Image backgrounds stretch to the widget box.

.background(Color.fromHex('#00FF88'));

.gradient([c1, c2], angle?)

Fill. Image backgrounds stretch to the widget box.

let result = .gradient('demo', 'demo', 'demo'); console.log(result);

.setBackgroundImage(name)

Fill. Image backgrounds stretch to the widget box.

.setBackgroundImage('demo');

.setImage(name)

Image widgets. setImage swaps the picture, scaleMode is fit, fill, stretch or center, and tint recolours it, which is how one white icon becomes a green one and a red one.

.setImage('demo');

.scaleMode(mode)

Image widgets. setImage swaps the picture, scaleMode is fit, fill, stretch or center, and tint recolours it, which is how one white icon becomes a green one and a red one.

let result = .scaleMode('safe'); console.log(result);

.tint(color)

Image widgets. setImage swaps the picture, scaleMode is fit, fill, stretch or center, and tint recolours it, which is how one white icon becomes a green one and a red one.

let result = .tint(Color.fromHex('#00FF88')); console.log(result);

.corner(r)

Decoration. .corner() also takes [tl, tr, br, bl]. On a text widget the shadow falls on the glyphs; on any box (panel, group, button, image) it is a drop shadow. .shadow(false) removes it.

let result = .corner(255); console.log(result);

.border(width, color)

Decoration. .corner() also takes [tl, tr, br, bl]. On a text widget the shadow falls on the glyphs; on any box (panel, group, button, image) it is a drop shadow. .shadow(false) removes it.

let result = .border('demo', Color.fromHex('#00FF88')); console.log(result);

.shadow(radius, dx, dy, color)

Decoration. .corner() also takes [tl, tr, br, bl]. On a text widget the shadow falls on the glyphs; on any box (panel, group, button, image) it is a drop shadow. .shadow(false) removes it.

let result = .shadow('demo', 'demo', 'demo', Color.fromHex('#00FF88')); console.log(result);

.opacity(0..1)

Compositing. Widgets are fully opaque by default; set opacity, or an alpha in the colour (#CC101317), when you want to see through them.

let result = .opacity('demo'); console.log(result);

.rotation(deg)

Compositing. Widgets are fully opaque by default; set opacity, or an alpha in the colour (#CC101317), when you want to see through them.

let result = .rotation('demo'); console.log(result);

.elevation(px)

Compositing. Widgets are fully opaque by default; set opacity, or an alpha in the colour (#CC101317), when you want to see through them.

let result = .elevation('demo'); console.log(result);

.setTextColor(c)

Text. family is system, mono, serif or condensed.

.setTextColor('demo');

.setTextSize(sp)

Text. family is system, mono, serif or condensed.

.setTextSize('demo');

.font(family)

Text. family is system, mono, serif or condensed.

let result = .font('demo'); console.log(result);

.bold(b)

Text. family is system, mono, serif or condensed.

let result = .bold(255); console.log(result);

.italic(b)

Text. family is system, mono, serif or condensed.

let result = .italic(255); console.log(result);

.align(a)

Text. family is system, mono, serif or condensed.

let result = .align(255); console.log(result);

.maxLines(n, ellipsize?)

Text. family is system, mono, serif or condensed.

let result = .maxLines('demo', 'demo'); console.log(result);

.html(text)

Rich text. HTML is sanitised to a safe tag subset; markdown supports **bold**, *italic*, __underline__ and backtick code. .plain() sets the text and turns rich formatting back off, so markup arriving from a variable is shown literally instead of being parsed.

let result = .html('Daily Reward Ready'); console.log(result);

.markdown(text)

Rich text. HTML is sanitised to a safe tag subset; markdown supports **bold**, *italic*, __underline__ and backtick code. .plain() sets the text and turns rich formatting back off, so markup arriving from a variable is shown literally instead of being parsed.

let result = .markdown('Daily Reward Ready'); console.log(result);

.plain(text)

Rich text. HTML is sanitised to a safe tag subset; markdown supports **bold**, *italic*, __underline__ and backtick code. .plain() sets the text and turns rich formatting back off, so markup arriving from a variable is shown literally instead of being parsed.

let result = .plain('Daily Reward Ready'); console.log(result);

.value(v)

Progress, gauge and sparkline data.

let result = .value('demo'); console.log(result);

.range(min, max)

Progress, gauge and sparkline data.

let result = .range(1, 10); console.log(result);

.progress(cur, total)

Progress, gauge and sparkline data.

let result = .progress('demo', 5); console.log(result);

.push(sample)

Progress, gauge and sparkline data.

let result = .push('demo'); console.log(result);

.samples([...])

Progress, gauge and sparkline data.

let result = .samples('demo'); console.log(result);

.colors(fill, track)

Progress, gauge and sparkline data.

let result = .colors('demo', 'demo'); console.log(result);

.showLabel(b)

Progress, gauge and sparkline data.

.showLabel(255);

.show()

Visibility and lifetime. .showFor auto-hides without blocking your script.

.show();

.hide()

Visibility and lifetime. .showFor auto-hides without blocking your script.

.hide();

.off()

Visibility and lifetime. .showFor auto-hides without blocking your script.

let result = .off(); console.log(result);

.hidden(b)

Visibility and lifetime. .showFor auto-hides without blocking your script.

let result = .hidden(255); console.log(result);

.visible(b)

Visibility and lifetime. .showFor auto-hides without blocking your script.

let result = .visible(255); console.log(result);

.showFor(ms)

Visibility and lifetime. .showFor auto-hides without blocking your script.

.showFor(1711360800000);

.remove()

Visibility and lifetime. .showFor auto-hides without blocking your script.

.remove();

.fadeIn(ms)

Animation.

let result = .fadeIn(1711360800000); console.log(result);

.fadeOut(ms)

Animation.

let result = .fadeOut(1711360800000); console.log(result);

.pulse({ times, period })

Animation.

let result = .pulse('demo', 'demo'); console.log(result);

Interactive widgets

Mark a widget clickable, long-pressable, double-tappable, draggable or resizable and it receives touches; touches outside it still reach the app underneath. Your macro’s own taps never hit your overlay.

.clickable(b)

Taps. button widgets have this on already.

.clickable(255);

.longPressable(b)

Long presses, read with .wasLongPressed().

let result = .longPressable(255); console.log(result);

.doubleTappable(b)

Double taps, read with .wasDoubleTapped().

let result = .doubleTappable(255); console.log(result);

.draggable(b)

The user can move the widget.

let result = .draggable(255); console.log(result);

.resizable(b)

The user can resize it.

let result = .resizable(255); console.log(result);

Each takes true when called with no argument, so .draggable() and .draggable(true) are the same, and .draggable(false) turns it off.

var btn = Ui.Overlay.button("pause") .setText("Pause") .at(0, "12%").anchor("bottom") .draggable(true) .show(); while (running) { if (btn.wasClicked()) { paused = !paused; } wait(50); }

Poll these. Each read consumes the event, so a click is reported once:

.wasClicked()

true once per gesture.

let result = .wasClicked(); console.log(result);

.wasLongPressed()

true once per gesture.

let result = .wasLongPressed(); console.log(result);

.wasDoubleTapped()

true once per gesture.

let result = .wasDoubleTapped(); console.log(result);

.getClickState()

1 if clicked since the last call, else 0.

let result = .getClickState(); console.log(result);

.getMoveState()

0 idle, 1 being dragged, 2 just released (consumed on read).

let result = .getMoveState(); console.log(result);

.getResizeState()

The same three states for resizing.

let result = .getResizeState(); console.log(result);

.getRegion()

A Region with the widget’s live on-screen position, or null before it has rendered.

let result = .getRegion(); console.log(result);

.getX()

Individual geometry reads.

let result = .getX(); console.log(result);

.getY()

Individual geometry reads.

let result = .getY(); console.log(result);

.getW()

Individual geometry reads.

let result = .getW(); console.log(result);

.getH()

Individual geometry reads.

let result = .getH(); console.log(result);

.getText()

Individual geometry reads.

let result = .getText(); console.log(result);

.isRendered()

Individual geometry reads.

let result = .isRendered(); console.log(result);

.exists()

Individual geometry reads.

let result = .exists(); console.log(result);

.info()

All of the above in one call: { x, y, w, h, text, exists, rendered }. Faster than several separate getters, so use it in loops.

let result = .info(); console.log(result);

A dragged widget stays where the user left it, and getRegion() reflects that immediately, so the rest of a menu can follow a dragged button.

.onClick(fn), .onLongPress(fn), .onDoubleTap(fn) and .onDragEnd(fn) register callbacks, and each turns the matching gesture on for you, so .onClick(fn) implies .clickable(true). .on(type, fn) takes any event name from the queue list below, including dragstart, resizestart and resizeend.

Handlers do not interrupt a running script. They run when you call Ui.Overlay.pump() or Ui.Overlay.waitForEvent(id?, timeoutMs?). Polling with wasClicked() needs neither and is simpler inside a loop.

Queue-level access: Ui.Overlay.pollEvent(), Ui.Overlay.pollEvents(), Ui.Overlay.clearEvents(). Events look like { id, type, x, y, at } where type is click, longpress, doubletap, dragstart, dragend, resizestart or resizeend.

Up to 24 widgets can be interactive at once; beyond that they render but do not receive touches.

Ui.Hud presets

Ui.Hud.card(opts)

Titled panel of label/value rows. The default way to show several numbers at once.

let result = Ui.Hud.card('demo'); console.log(result);

Ui.Hud.buttons(labels, opts?)

Themed column (or row) of buttons in one group.

let result = Ui.Hud.buttons('demo', 'demo'); console.log(result);

Ui.Hud.status(text, opts?)

Centred status pill at the top of the screen.

let result = Ui.Hud.status('Daily Reward Ready', 'demo'); console.log(result);

Ui.Hud.counter(name, value, opts?)

Labelled counter; repeated names stack down the left edge.

let result = Ui.Hud.counter('demo', 1, 'demo'); console.log(result);

Ui.Hud.timer(name, startedAtMillis, opts?)

Counter showing elapsed m:ss.

let result = Ui.Hud.timer('demo', 'demo', 'demo'); console.log(result);

Ui.Hud.progress(current, total, opts?)

Progress bar near the bottom of the screen.

let result = Ui.Hud.progress(1, 5, 'demo'); console.log(result);

Ui.Hud.chart(name, sample, opts?)

Sparkline that appends sample on each call.

let result = Ui.Hud.chart('demo', 'demo', 'demo'); console.log(result);

``Ui.Hud.theme("dark"`

"light"

let result = `Ui.Hud.theme('demo'); console.log(result);

Ui.Hud.clear()

Remove all overlay widgets.

Ui.Hud.clear();

Ui.Hud.card(opts) builds the whole card at once and lays it out in dp, so it looks the same on every device with no coordinates to choose.

let card = Ui.Hud.card({ title: "Farm run", rows: [["Rounds", 0], ["Gold", 0], ["Status", "starting"]] }); let btns = Ui.Hud.buttons(["Pause", "Stop"]); while (true) { if (btns.clicked() == "Stop") break; card.set("Rounds", rounds).set("Gold", gold); wait(500); } card.remove(); btns.remove();

opts: { title, rows, id, anchor, x, y, width, height, background, safeArea }. Rows accept ["Label", value] pairs, { label, value } objects, a plain { Label: value } object, or a bare string for a full-width line. Defaults: top-right, 12dp inset, themed background with 16dp corners, labels dimmed on the left and values in the accent colour, mono and right-aligned.

The default width adapts to the screen, so small phones get a narrower card. A width you pass is used as given. Ui.Hud.buttons adapts its default width the same way.

The card handle has set(labelOrIndex, value) (what belongs in the loop), label(row, text), setTitle(text), row(row) for the value widget’s handle, group, show(), hide() and remove(). Re-calling Ui.Hud.card with the same id updates the existing card rather than stacking a second one on top.

Ui.Hud.buttons(labels, opts?) takes ["Start", "Stop"] or [{ text, id }] and { id, anchor, x, y, width, height, gap, size, horizontal, background, safeArea }, defaulting to a bottom-right column of 132x44dp buttons. It returns the widget handles as an array that also carries clicked() (the label tapped since the last call, else null), group and remove().

Both presets follow Ui.Hud.theme(...), so setting the theme first restyles them.

Porting from OnScreenText

Scripts written against other automation apps’ OnScreenText run with minimal changes. OnScreenText(x, y, w, h, fullscreen?) and OnScreenText(region, fullscreen?) are supported and create the widget hidden, as they do there. setText, setBackgroundImage, setBackgroundColor, setTextColor, setTextSize, setX/setY/setW/setH, clickable, moveable, show, off, hidden, getRegion, getClickState and getMoveState all behave the same way, and Region supports getX()/getY()/getW()/getH().

var menu = OnScreenText(25, 200, 110, 110); menu.setBackgroundImage("menu_icon"); menu.clickable(true); menu.moveable(true); menu.show(); while (true) { if (menu.getMoveState() === 1) { /* being dragged */ } if (menu.getClickState() === 1) { toggleMenu(); } wait(30); }

Images are looked up in your saved templates by name ("menu_icon", with or without an extension), or you can pass an absolute file path or a data:/base64: string.