Builtin Library
README.md
text_observe
What the text system is holding, and which face a label actually shaped with.
The engine lays every text object out against its font database and keeps the result. This module publishes that: how many text objects are live and what they are, the entity each one belongs to, the runtime texture its raster landed in with the bytes it costs, and — the part that exists nowhere else — the font face the shaper reached for, which is a different question from the family the style asked for.
Backed by the __textObserve readings the engine alone can take, joined here
with the entity, renderer and font surfaces already in Luau. The public entries
are text.observe, text.count, text.alive, text.face, text.faceReasons,
text.orphans, text.rasterMemory, font.observe and font.reconcile.
/zero/runtime/text serves the engine's reading before those joins — one
serialisation, so a column present in both carries the same value in each.
owner.alive, raster.bytes, face.reason and orphans are the joins this
module adds to it.
Nothing here runs per frame — the reading is built when a caller asks, which is also what makes it as current in edit mode as in play.
Readings
| Call | Answers |
|---|---|
observe() | every live text object, each with its face, its owner entity and that entity's liveness, and the raster texture its bytes are in; plus orphans, fonts and the glyph-raster total |
face(handle) | one label: requested, resolved, matched, and a reason from faceReasons() |
orphans() | the text objects whose owning entity is gone |
rasterMemory() | glyph-raster bytes, taken out of the one texture map renderer.gpuMemory() totals its textures figure from |
reconcile() | every registered family held against what the shaper does with it |
Why a label is not in the face it named
faceReasons() enumerates the whole set, nearest cause first:
| Reason | Meaning |
|---|---|
resolved | the face the shaper used IS the family requested |
noFamilyRequested | the style named no family, so the shaper took the default |
notShaped | the object produced no glyphs, so there is no face either way |
familyUnknown | the requested family is in no registry entry, so there was nothing to match |
familyNotSelectable | the family IS registered, and the shaper still answered with a different face |
familyCoveredNoGlyph | the family is one the shaper's font database holds, and its faces cover none of the glyphs asked for — reconcile() reports it, having laid the family out under its own weights and over several scripts |
Example
-- Why is this label in the wrong font?
local h = text.create({ content = "Title", fontFamily = "Inter" })
local face = text.face(h)
if face.reason == "familyUnknown" or face.reason == "familyNotSelectable" then
print(face.requested .. " asked for, " .. face.resolved .. " used — " .. face.reason)
end
-- What is the engine still holding for entities that are gone?
for _, row in ipairs(text.orphans()) do
print(row.content .. " belongs to " .. row.owner.entityId .. ", which no longer exists")
end
-- What do glyph rasters cost, out of the pool they live in?
local mem = text.rasterMemory()
print(mem.bytes .. " of " .. mem.poolBytes .. " bytes of " .. mem.pool)