This happens reliably every 2 or three times running this:
--# Main
-- Codea 4.x -- Style & Text API Reference (single tab, scrollable)
-- Green monospace = syntax. White below it = live result.
-- Drag up/down to scroll.
CODE_FONT = "Courier New" -- try "Menlo" or "Courier" if unavailable
CODE_SIZE = 13
CODE_LINE_H = 16
MARGIN = 20
GAP = 18
PAD = 14
HEADER_H = 70
-- WORKAROUND: "LEFT | TOP" rendered right-anchored on device (text
-- hung off the left edge). Using TOP alone appears left-anchored by
-- default. If text now centers instead, try LT = RIGHT | TOP, or
-- flip back to LT = LEFT | TOP once Two Lives Left confirms which
-- global is colliding.
LT = TOP
function setup()
scrollY = 0
lastWidth = WIDTH
text.style.wave = function(tag, format)
format.callback = function(str, i, mod)
mod.offsetY = math.sin(time.elapsed * 4 + i) * 6
end
end
sections = buildSections()
layoutSections()
end
function draw()
background(18, 18, 22)
if WIDTH ~= lastWidth then
lastWidth = WIDTH
layoutSections()
end
matrix.push()
matrix.translate(0, scrollY)
drawHeader()
for _, s in ipairs(sections) do
drawSection(s)
end
matrix.pop()
drawScrollHint()
end
function touched(touch)
if touch.state == MOVING then
scrollY = scrollY + touch.deltaY
local maxScroll = math.max(0, contentHeight - HEIGHT)
end
end
-- Layout / scaffolding ------------------------------------------------
function drawHeader()
style.push()
style.fill(255)
style.fontSize(24)
style.textStyle(TEXT_BOLD)
style.textAlign(LT)
text("CODEA 4.x -- STYLE & TEXT REFERENCE", MARGIN, HEIGHT - MARGIN)
style.fontSize(14)
style.textStyle(TEXT_NORMAL)
style.fill(180)
text("Drag to scroll. Green = syntax, white = live result.",
MARGIN, HEIGHT - MARGIN - 30)
style.pop()
end
function drawScrollHint()
if contentHeight <= HEIGHT then return end
style.push()
style.noStroke()
style.fill(255, 255, 255, 40)
rect(WIDTH - 6, 0, 4, HEIGHT)
local maxScroll = contentHeight - HEIGHT
local thumbH = math.max(30, HEIGHT * HEIGHT / contentHeight)
local t = -scrollY / maxScroll
local thumbY = HEIGHT - thumbH - t * (HEIGHT - thumbH)
style.fill(255, 255, 255, 140)
rect(WIDTH - 6, thumbY, 4, thumbH)
style.pop()
end
function addSection(list, title, code, h, drawFn)
table.insert(list, {title = title, code = code, h = h, draw = drawFn})
end
function countLines(code)
local n = 0
for _ in (code .. "\n"):gmatch("(.-)\n") do n = n + 1 end
return n
end
function layoutSections()
local docY = MARGIN + HEADER_H
for _, s in ipairs(sections) do
s.docY = docY
s.codeH = countLines(s.code) * CODE_LINE_H
s.totalH = 22 + s.codeH + 10 + s.h + GAP
docY = docY + s.totalH
end
contentHeight = docY + MARGIN
end
function drawCode(code, x, y, w)
style.push()
style.font(CODE_FONT)
style.fontSize(CODE_SIZE)
style.fill(70, 220, 110)
style.textAlign(LT)
style.textStyle(TEXT_NORMAL)
local cy = y
for line in (code .. "\n"):gmatch("(.-)\n") do
text(line, x, cy)
cy = cy - CODE_LINE_H
end
style.pop()
end
function drawSection(s)
local x = MARGIN
local w = WIDTH - MARGIN * 2
local topY = HEIGHT - s.docY
style.push()
style.fill(255)
style.fontSize(17)
style.textStyle(TEXT_BOLD)
style.textAlign(LT)
text(s.title, x, topY)
style.pop()
local codeY = topY - 22
drawCode(s.code, x, codeY, w)
local panelTop = codeY - s.codeH - 10
local panelBottom = panelTop - s.h
style.push()
style.noFill()
style.stroke(60, 60, 75)
style.strokeWidth(1)
rect(x, panelBottom, w, s.h)
style.pop()
s.draw(x + PAD, panelBottom + PAD, w - PAD * 2, s.h - PAD * 2)
end
-- Demo sections ---------------------------------------------------------
function buildSections()
local list = {}
addSection(list, "style.push / pop / reset / get / set", [[
local saved = style.get()
style.fill(255, 90, 90)
ellipse(cx1, cy, 70)
style.pop()
style.set(saved)
style.fill(90, 200, 255)
ellipse(cx2, cy, 70)]], 130, function(x, y, w, h)
local cy = y + h / 2
style.push()
local saved = style.get()
style.fill(255, 90, 90)
ellipse(x + w * 0.25, cy, 70)
style.pop()
style.set(saved)
style.fill(90, 200, 255)
ellipse(x + w * 0.75, cy, 70)
end)
addSection(list, "style.fill / noFill", [[
style.fill(255, 200, 0)
rect(x1, y, 90, 90)
style.noFill()
style.stroke(255)
style.strokeWidth(3)
rect(x2, y, 90, 90)]], 130, function(x, y, w, h)
style.push()
style.fill(255, 200, 0)
rect(x, y, 90, 90)
style.noFill()
style.stroke(255)
style.strokeWidth(3)
rect(x + w - 90, y, 90, 90)
style.pop()
end)
addSection(list, "style.stroke / noStroke / strokeWidth", [[
style.noFill()
style.stroke(100, 200, 255)
style.strokeWidth(2)
ellipse(cx1, cy, 90)
style.strokeWidth(12)
ellipse(cx2, cy, 90)]], 130, function(x, y, w, h)
style.push()
style.noFill()
style.stroke(100, 200, 255)
local cy = y + h / 2
style.strokeWidth(2)
ellipse(x + w * 0.25, cy, 90)
style.strokeWidth(12)
ellipse(x + w * 0.75, cy, 90)
style.pop()
end)
addSection(list, "style.lineCap: ROUND / SQUARE / PROJECT", [[
style.stroke(255)
style.strokeWidth(16)
style.lineCap(ROUND)
line(x1, y1, x2, y1)
style.lineCap(SQUARE)
line(x1, y2, x2, y2)
style.lineCap(PROJECT)
line(x1, y3, x2, y3)]], 130, function(x, y, w, h)
style.push()
style.stroke(255)
style.strokeWidth(16)
local x1, x2 = x + 20, x + w - 20
style.lineCap(ROUND)
line(x1, y + h - 20, x2, y + h - 20)
style.lineCap(SQUARE)
line(x1, y + h / 2, x2, y + h / 2)
style.lineCap(PROJECT)
line(x1, y + 20, x2, y + 20)
style.pop()
end)
addSection(list, "style.lineJoin: ROUND / MITER / BEVEL", [[
style.noFill()
style.stroke(255)
style.strokeWidth(14)
style.lineJoin(ROUND)
polyline(x1, y1, x1 + 40, y1 + 50, x1 + 80, y1)]], 130, function(x, y, w, h)
style.push()
style.noFill()
style.stroke(255)
style.strokeWidth(14)
local x1 = x + 10
local step = (w - 20) / 3
style.lineJoin(ROUND)
polyline(x1, y + 10, x1 + step * 0.5, y + h - 20, x1 + step, y + 10)
x1 = x1 + step
style.lineJoin(MITER)
polyline(x1, y + 10, x1 + step * 0.5, y + h - 20, x1 + step, y + 10)
x1 = x1 + step
style.lineJoin(BEVEL)
polyline(x1, y + 10, x1 + step * 0.5, y + h - 20, x1 + step, y + 10)
style.pop()
end)
addSection(list, "style.shapeMode: CORNER / CORNERS / CENTER / RADIUS", [[
style.shapeMode(CORNER)
rect(x, y, 60, 60)
style.shapeMode(CENTER)
rect(x, y, 60, 60)
style.shapeMode(RADIUS)
rect(x, y, 60, 60)]], 150, function(x, y, w, h)
style.push()
style.fill(119, 190, 226, 34)
local step = w / 4
local labels = {"CORNER", "CORNERS", "CENTER", "RADIUS"}
local modes = {CORNER, CORNERS, CENTER, RADIUS}
for i = 2,4 do
local px = x + step * (i - 1) + step / 2
style.shapeMode(modes[i])
rect(px, y + h / 2, 50, 50)
end
style.shapeMode(CORNER)
style.fill(255)
style.fontSize(11)
style.textAlign(CENTER | BOTTOM)
for i = 1, 4 do
local px = x + step * (i - 1) + step / 2
text(labels[i], px, y + 4)
end
style.pop()
end)
addSection(list, "style.tint / pixelScaling (sprite)", [[
style.tint(255, 100, 100)
sprite(asset.builtin.Cargo_Bot.Codea_Icon, x1, y, 80, 80)
style.tint(255)
style.pixelScaling(0.5)
sprite(asset.builtin.Cargo_Bot.Codea_Icon, x2, y, 80, 80)]], 130, function(x, y, w, h)
style.push()
local cy = y + h / 2
style.tint(255, 100, 100)
sprite(asset.builtin.Cargo_Bot.Codea_Icon, x + w * 0.25, cy, 80, 80)
style.tint(255)
style.pixelScaling(0.5)
sprite(asset.builtin.Cargo_Bot.Codea_Icon, x + w * 0.75, cy, 80, 80)
style.pop()
end)
addSection(list, "style.blend: NORMAL / ADDITIVE / MULTIPLY / SCREEN", [[
style.blend(NORMAL)
style.fill(255, 0, 0, 180)
ellipse(ax, y, 80)
style.fill(0, 255, 0, 180)
ellipse(bx, y, 80)]], 160, function(x, y, w, h)
style.push()
local labels = {"NORMAL", "ADDITIVE", "MULTIPLY", "SCREEN"}
local modes = {NORMAL, ADDITIVE, MULTIPLY, SCREEN}
local step = w / 4
for i = 1, 4 do
local cx = x + step * (i - 1) + step / 2
local cy = y + h / 2 + 10
style.blend(modes[i])
style.fill(255, 60, 60, 180)
ellipse(cx - 15, cy, 55)
style.fill(60, 255, 90, 180)
ellipse(cx + 15, cy, 55)
end
style.blend(NORMAL)
style.fill(255)
style.fontSize(10)
style.textAlign(CENTER | BOTTOM)
for i = 1, 4 do
local cx = x + step * (i - 1) + step / 2
text(labels[i], cx, y)
end
style.pop()
end)
addSection(list, "style.clip / noClip", [[
style.clip(cx, cy, cw, ch)
style.fill(255, 200, 0)
rect(x, y, w, h)
style.noClip()]], 140, function(x, y, w, h)
style.push()
style.clip(x + w * 0.25, y + 10, w * 0.5, h - 20)
style.fill(255, 200, 0)
rect(x, y, w, h)
style.noClip()
style.noFill()
style.stroke(255)
style.strokeWidth(1)
rect(x + w * 0.25, y + 10, w * 0.5, h - 20)
style.pop()
end)
addSection(list, "style.stencil (mask a shape against another)", [[
style.stencil{reference = 1, pass = STENCIL_OP_REPLACE}
style.blend(DISABLED)
ellipse(cx, cy, 90)
style.blend(NORMAL)
style.stencil{reference = 1, condition = STENCIL_TEST_EQUAL}
-- only pixels inside the ellipse are drawn below
rect(bx, by, bw, bh)]], 150, function(x, y, w, h)
style.push()
local cx, cy = x + w / 2, y + h / 2
if style.stencil then
style.stencil{reference = 1, pass = STENCIL_OP_REPLACE}
style.blend(DISABLED)
ellipse(cx, cy, 100)
style.blend(NORMAL)
style.stencil{reference = 1, condition = STENCIL_TEST_EQUAL}
local colors = {{255, 90, 90}, {255, 200, 60}, {90, 200, 255}, {150, 255, 120}}
local barW = w / #colors
for i = 1, #colors do
local c = colors[i]
style.fill(c[1], c[2], c[3])
rect(x + barW * (i - 1), y, barW, h)
end
else
style.fill(180)
style.fontSize(14)
style.textAlign(LT)
text("style.stencil not available on this build", x, y + h - 6, w)
end
style.pop()
end)
addSection(list, "style.font / fontSize / textAlign anchors", [[
style.font("Helvetica")
style.fontSize(18)
style.textAlign(LEFT | TOP)
text("Left/Top", ax, ay)
style.textAlign(CENTER | MIDDLE)
text("Center/Middle", bx, by)
style.textAlign(RIGHT | BOTTOM)
text("Right/Bottom", cx, cy)]], 170, function(x, y, w, h)
style.push()
style.font("Helvetica")
style.fontSize(15)
style.fill(255)
local pts = {
{x + 10, y + h - 10, LT},
{x + w / 2, y + h / 2, CENTER | MIDDLE},
{x + w - 10, y + 10, RIGHT | BOTTOM},
}
local labels = {"Left/Top", "Center/Middle", "Right/Bottom"}
for i, p in ipairs(pts) do
style.textAlign(p[3])
text(labels[i], p[1], p[2])
style.push()
style.fill(255, 90, 90)
ellipse(p[1], p[2], 6)
style.pop()
end
style.pop()
end)
addSection(list, "style.textStyle flags", [[
style.textStyle(TEXT_BOLD)
style.textStyle(TEXT_ITALICS)
style.textStyle(TEXT_UNDERLINE)
style.textStyle(TEXT_OVERLINE)
style.textStyle(TEXT_STRIKE_THROUGH)
style.textStyle(TEXT_BACKGROUND)
style.textStyle(TEXT_UPPERCASE)
style.textStyle(TEXT_LOWERCASE)
style.textStyle(TEXT_NATIVE)]], 260, function(x, y, w, h)
style.push()
style.fontSize(16)
style.textAlign(LT)
local rows = {
{TEXT_BOLD, "Bold sample"},
{TEXT_ITALICS, "Italic sample"},
{TEXT_UNDERLINE, "Underlined sample"},
{TEXT_OVERLINE, "Overlined sample"},
{TEXT_STRIKE_THROUGH, "Strike-through sample"},
{TEXT_BACKGROUND, "Background sample"},
{TEXT_UPPERCASE, "shown uppercase"},
{TEXT_LOWERCASE, "SHOWN LOWERCASE"},
{TEXT_NATIVE, "Native renderer sample"},
}
local rowH = h / #rows
for i, r in ipairs(rows) do
style.fill(255)
style.textStyle(r[1])
text(r[2], x, y + h - rowH * (i - 1))
end
style.pop()
end)
addSection(list, "text() rich formatting: built-in <b> / <i> tags", [[
style.textStyle(TEXT_RICH)
text("The <i>quick brown fox</i> jumps over " ..
"the <b>lazy dog</b>.", x, y, w)]], 90, function(x, y, w, h)
style.push()
style.fill(255)
style.fontSize(17)
style.textAlign(LT)
style.textStyle(TEXT_RICH)
text("The <i>quick brown fox</i> jumps over " ..
"the <b>lazy dog</b>.", x, y + h, w)
style.pop()
end)
addSection(list, "Custom rich-text tag (per-glyph callback)", [[
-- registered once in setup():
text.style.wave = function(tag, format)
format.callback = function(str, i, mod)
mod.offsetY = math.sin(time.elapsed*4 + i) * 6
end
end
style.textStyle(TEXT_RICH)
text("<wave>This text waves</wave> as it renders", x, y)]], 90, function(x, y, w, h)
style.push()
style.fill(90, 220, 255)
style.fontSize(19)
style.textAlign(LT)
style.textStyle(TEXT_RICH)
text("<wave>This text waves</wave> as it renders", x, y + h - 10)
style.pop()
end)
addSection(list, "text() wrapping in a fixed box, textSize() query", [[
local msg = "This paragraph is long enough that it " ..
"wraps across multiple lines inside the box."
local tw, th = textSize(msg, boxW)
text(msg, x, y, boxW, boxH)]], 220, function(x, y, w, h)
style.push()
style.fill(255)
style.fontSize(16)
style.textAlign(LT)
style.textStyle(TEXT_NORMAL)
local msg = "This paragraph is long enough that it " ..
"wraps across multiple lines inside the box."
local boxW = w
local tw, th = textSize(msg, boxW)
text(msg, x, y + h, boxW, h)
style.noFill()
style.stroke(90, 200, 255)
style.strokeWidth(1)
rect(x, y + h - th, boxW, th)
style.fill(90, 200, 255)
style.fontSize(12)
text(string.format("measured: %.0f x %.0f", tw, th), x, y)
style.pop()
end)
addSection(list, "Reference only: viewRect / sortOrder / colorspace", [[
-- these affect the raw device viewport / render queue,
-- not a local drawing area, so they are not fired live
-- inside this scrolling demo:
style.viewRect(x, y, w, h) -- sets the GL viewport
style.sortOrder(order) -- controls 3D draw ordering
colorspace(LINEAR) -- GAMMA or LINEAR color space]], 90, function(x, y, w, h)
style.push()
style.fill(180)
style.fontSize(14)
style.textAlign(LT)
text("(shown for reference only -- see comment above)", x, y + h - 6, w)
style.pop()
end)
return list
end
Only fixed by terminating and restarting.

