Brodgar client

hafen.ui: Design Pixels

Every coordinate and size a widget takes or gives is a design pixel. That is one pixel before the client applies the user’s interface scale, the unit the client’s own layout and art are written in.

local panel = hafen.ui():widget():size(100, 40):position(30, 20)
panel:size()                 -- {w = 100, h = 40}, on every client
hafen.ui():scale()           -- 1.5 on a client the user scaled up; nothing multiplies by it

Read

Method Returns Permission Description
hafen.ui():scale() number Unprotected The interface scale in force, 1.0 or more. A diagnostic to print, not a unit: the numbers are already right.
hafen.ui():scale(v) — — Raises, naming hafen.client():options():interface():scale(v), the user’s preference, which takes a client restart. Until then the two differ.

What the unit covers

Where Rule
Widget geometry :position(x, y), :size(w, h), :rootPos(), :info().pos, :info().size — a size in w/h, a place in x/y. What you write is what you read back, at any scale: the conversion happens once, at the edge, and is exact in that direction. A place on the HUD or in the layer then follows a resize of the game window, and reads back where it has moved to, unless the widget covers the screen (native).
The screen’s questions The box a widget occupies, the point hafen.ui():hit(x, y) tests, the pointer, and the :x()/:y() an input event carries are one space. None is reached through a Session, since the client draws one screen however many characters it holds.
The world s:world():worldToScreen(p) answers a root pair in this unit, and s:world():screenToWorld(pt, fn) takes that table back. A projected point is hit-tested, drawn and fed to the ground with nothing between.
Drawing Every coordinate, width and radius a g: verb takes. The :w()/:h() a Draw reports. The w, h an overlay painter gets. The screen_x, screen_y at a gob overlay.
The stylesheet A rule’s position and size, an anchor’s offset, a padding, a border’s slice insets, a listbox’s :rowHeight(n) and a grid’s :cellSize(w, h). A theme is a set of numbers meaning one thing on every client.
A control’s height A fact of the client’s pictures, not a number you write. :size(w) takes the width alone. A column stacks controls without adding heights up. :pack() sizes a window around them.
Your images A 32×32 PNG is 32×32 to img:size() and covers 32 design pixels drawn, at every scale.
The client’s art Authored several times larger and resampled once per scale. A {res = …} in a bg or border is measured and sliced in the same unit. It is crisper than a file of that apparent size on a scaled-up client (naming a picture).
A font’s size A design pixel too: a 14 px caption fits a 20 px row on every client — hafen.font.
local mouse = hafen.ui():mouse()
assert(hafen.ui():hit(mouse:x(), mouse:y()) == mouse:over())   -- one pair, asked two ways

local world = hafen.session():current():world()
world:screenToWorld({x = mouse:x(), y = mouse:y()}, function(position)   -- the ground under the cursor, a frame later
  if position then hafen.log():write("standing spot: " .. tostring(position:tileCoord().x)) end
end)

hafen.ui():sheet():rule("window[title=Equipment]"):position(40, 200):install()
local equipment_position = hafen.session():current():ui():match("window[title=Equipment]"):position()   -- {x = 40, y = 200}, at any scale

The inverse conversion is not exact. A widget the client placed does not generally sit on a whole design pixel. Reading a client widget’s position and writing the same pair back may shift it by less than one design pixel. Hand a place back with :position(nil), not by rewriting the numbers.


See Also