Colored text

Colour belongs to the draw, not to the string: each run is natiny.render.set_color followed by natiny.render.text, with natiny.font.measure_text moving the pen to the next one. The same runs are drawn twice, once dark and offset, for the shadow.


    
main.lua
local natiny = require("natiny")

natiny.backend.init()

local win = natiny.window.create("Colored text", 900, 600)
natiny.window.set_scale_mode(win, natiny.window.SCALE_MODE_STRETCH)

local res  = natiny.resource.load("common/fonts/roboto.ttf")
local font = natiny.font.create(res, 26)
local small = natiny.font.create(res, 18)

local BG = natiny.color.from_hex("#151E2C")
local PANEL = natiny.color.from_hex("#0F1621")
local LIGHT_TEXT = natiny.color.from_hex("#F3F5FA")
-- One colour to a kind of token. There is no rich text: a run is a colour and
-- a string, and the pen keeps its place between them.
local INK = {
    keyword = natiny.color.from_hex("#C692E9"),
    call    = natiny.color.from_hex("#7ABEFF"),
    string  = natiny.color.from_hex("#A8C238"),
    number  = natiny.color.from_hex("#F4B266"),
    comment = natiny.color.from_hex("#6E7A8C"),
    plain   = LIGHT_TEXT,
}
local SHADOW_INK = natiny.color.from_hex("#00000080")

local LINES = {
    { { "-- one colour, one draw, one pen", "comment" } },
    { { "local ", "keyword" }, { "message ", "plain" }, { "= ", "plain" },
      { "\"hello\"", "string" } },
    { { "for ", "keyword" }, { "i ", "plain" }, { "= ", "plain" },
      { "1", "number" }, { ", ", "plain" }, { "8 ", "number" },
      { "do", "keyword" } },
    { { "    print", "call" }, { "(message, i * ", "plain" },
      { "1.5", "number" }, { ")", "plain" } },
    { { "end", "keyword" } },
}

local X, Y = 90, 220
local SHADOW = 3

-- Draws one line of runs and answers how wide it turned out. Every run is an
-- ordinary text draw; measure_text is what moves the pen past the last one.
local function draw_line(y, runs, color)
    local pen = X
    for _, run in ipairs(runs) do
        local ink = color or INK[run[2]]
        natiny.render.set_color(ink)
        natiny.render.text(font, pen, y, run[1])
        pen = pen + natiny.font.measure_text(font, run[1])
    end
    return pen - X
end

natiny.backend.loop(function(dt)
    natiny.window.bind(win, function()
        local line_height = natiny.font.get_line_height(font)
        local height = line_height * #LINES

        natiny.render.clear(BG)

        natiny.render.set_color(PANEL)
        natiny.render.rounded_rectangle(X - 30, Y - 26, 720, height + 52, 10)
        natiny.render.set_color(natiny.color.from_hex("#A9B3C540"))
        natiny.render.rounded_rectangle_outline(X - 30, Y - 26, 720, height + 52, 10, 10, 10, 10, 2)

        for i, runs in ipairs(LINES) do
            local y = Y + (i - 1) * line_height
            -- The same runs twice: a dark pass offset behind, then the colours.
            draw_line(y + SHADOW, runs, SHADOW_INK)
            draw_line(y, runs)
        end

        natiny.render.set_color(LIGHT_TEXT)
        natiny.render.text(small, X - 30, 70,
            "set_color, text, set_color, text - the colour belongs to the draw")

        local legend, pen = { "keyword", "call", "string", "number", "comment" }, X - 30
        for _, kind in ipairs(legend) do
            local ink = INK[kind]
            natiny.render.set_color(ink)
            natiny.render.rectangle(pen, Y + height + 78, 12, 12)
            natiny.render.text(small, pen + 20, Y + height + 72, kind)
            pen = pen + 40 + natiny.font.measure_text(small, kind)
        end
    end)
end)

natiny.backend.shutdown()