A text field

An editable UTF-8 text field that uses natiny.font.offset_at to place the caret from a mouse click and natiny.font.measure_text to draw it. Type or click inside the field, and use Backspace, Delete, arrow, Home, and End keys to edit the text.


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

natiny.backend.init()

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

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

local BG     = {  21 / 255,  30 / 255,  44 / 255 } -- #151E2C
local OBJECT = { 169 / 255, 179 / 255, 197 / 255 } -- #A9B3C5
local ACCENT = { 248 / 255, 249 / 255, 253 / 255 } -- #F8F9FD
local LIGHT_TEXT = { 243 / 255, 245 / 255, 250 / 255 } -- #F3F5FA
local DARK_TEXT = { 17 / 255, 24 / 255, 39 / 255 } -- #111827
local X, Y, W, H = 60, 260, 780, 56
local PAD = 14

local value, caret, blink = "", 0, 0

local function prev_offset(offset)
    return offset > 0 and utf8.offset(value, -1, offset + 1) - 1 or 0
end

local function next_offset(offset)
    return offset < #value and utf8.offset(value, 2, offset + 1) - 1 or #value
end

natiny.backend.loop(function(dt)
    natiny.window.bind(win, function()
        blink = blink + dt

        for i = 0, natiny.input.get_char_count() - 1 do
            local code = natiny.input.get_char_at(i)
            if code >= 32 then
                local char = utf8.char(code)
                value = value:sub(1, caret) .. char .. value:sub(caret + 1)
                caret, blink = caret + #char, 0
            end
        end

        if natiny.input.get_key_pressed(natiny.input.KEY_BACKSPACE) and caret > 0 then
            local from = prev_offset(caret)
            value = value:sub(1, from) .. value:sub(caret + 1)
            caret, blink = from, 0
        end
        if natiny.input.get_key_pressed(natiny.input.KEY_DELETE) and caret < #value then
            value = value:sub(1, caret) .. value:sub(next_offset(caret) + 1)
        end
        if natiny.input.get_key_pressed(natiny.input.KEY_LEFT)  then caret, blink = prev_offset(caret), 0 end
        if natiny.input.get_key_pressed(natiny.input.KEY_RIGHT) then caret, blink = next_offset(caret), 0 end
        if natiny.input.get_key_pressed(natiny.input.KEY_HOME)  then caret, blink = 0, 0 end
        if natiny.input.get_key_pressed(natiny.input.KEY_END)   then caret, blink = #value, 0 end

        if natiny.input.get_mouse_button_pressed(natiny.input.MOUSE_BUTTON_LEFT) then
            local mx, my = natiny.input.get_mouse_x(), natiny.input.get_mouse_y()
            if mx >= X and mx <= X + W and my >= Y and my <= Y + H then
                caret = natiny.font.offset_at(font, value, mx - (X + PAD))
                blink = 0
            end
        end

        natiny.render.clear(BG[1], BG[2], BG[3], 1)

        natiny.render.set_color(OBJECT[1], OBJECT[2], OBJECT[3], 1)
        natiny.render.rounded_rectangle(X, Y, W, H, 6, 6, 6, 6)
        natiny.render.set_color(ACCENT[1], ACCENT[2], ACCENT[3], 1)
        natiny.render.rounded_rectangle_outline(X, Y, W, H, 6, 6, 6, 6, 3)

        local text_y = Y + (H - natiny.font.get_line_height(font)) / 2
        if value == "" then
            natiny.render.set_color(DARK_TEXT[1], DARK_TEXT[2], DARK_TEXT[3], 1)
            natiny.render.text(font, X + PAD, text_y, "type something - Unicode works too")
        else
            natiny.render.set_color(DARK_TEXT[1], DARK_TEXT[2], DARK_TEXT[3], 1)
            natiny.render.text(font, X + PAD, text_y, value)
        end

        if blink % 1.0 < 0.55 then
            local cx = X + PAD + natiny.font.measure_text(font, value:sub(1, caret))
            natiny.render.set_color(ACCENT[1], ACCENT[2], ACCENT[3], 1)
            natiny.render.rectangle(cx, text_y, 2, natiny.font.get_line_height(font))
        end

        natiny.render.set_color(LIGHT_TEXT[1], LIGHT_TEXT[2], LIGHT_TEXT[3], 1)
        natiny.render.text(font, X, 150, string.format("caret at byte %d of %d", caret, #value))
    end)
end)

natiny.backend.shutdown()