local fonts = require("fonts") local Textbox = {} Textbox.__index = Textbox function Textbox:new() local self = setmetatable({}, Textbox) self.active = false self.text = "" self.visibleText = "" self.mode = "show" self.x = 0 self.y = 0 self.align = "center" self.maxLines = nil self.maxChars = nil self.charIndex = 0 self.timer = 0 self.speed = 0.03 self.width = 0 self.height = 0 self.lines = {} return self end function Textbox:show(text, pos, mode, opts) opts = opts or {} self.text = text self.mode = mode or "show" self.x = pos[1] self.y = pos[2] self.align = pos[3] or "center" self.maxLines = opts.maxLines self.maxChars = opts.maxChars self.fontSize = opts.fontSize self.centeredText = opts.centeredText self.wrapToFit = opts.wrapToFit self.life = opts.life self.lifeTimer = 0 self.charIndex = 0 self.visibleText = "" self.timer = 0 self.active = true self:layout() end function Textbox:layout() self.maxLines = self.maxLines or 4 self.maxChars = self.maxChars or 40 self.font = self.fontSize and fonts.get(self.fontSize) or love.graphics.getFont() local padding = 10 if self.wrapToFit then local tempWidth = self.font:getWidth(string.rep("A", self.maxChars)) local _, lines = self.font:getWrap(self.text, tempWidth) self.lines = lines or {} self.lines = self.lines or {} local maxW = 0 for _, line in ipairs(self.lines) do local w = self.font:getWidth(line) if w > maxW then maxW = w end end local lineCount = math.max(1, #self.lines) self.width = maxW + padding * 2 self.height = self.font:getHeight() * lineCount + padding * 2 else self.width = self.font:getWidth(string.rep("A", self.maxChars)) + padding * 2 self.height = self.font:getHeight() * self.maxLines + padding * 2 end end function Textbox:wrap(text) local lines = {} local line = "" for word in text:gmatch("%S+") do if line == "" then line = word elseif #line + #word + 1 <= self.maxChars then line = line .. " " .. word else table.insert(lines, line) line = word end end if line ~= "" then table.insert(lines, line) end return lines end function Textbox:update(dt) if not self.active then return end if self.life then self.lifeTimer = self.lifeTimer + dt if self.lifeTimer >= self.life then self:hide() return end end if self.mode == "write" then self.timer = self.timer + dt if self.timer > self.speed then self.timer = 0 self.charIndex = self.charIndex + 1 self.visibleText = self.text:sub(1, self.charIndex) end else self.visibleText = self.text end end function Textbox:draw() if not self.active then return end local x = self.x local y = self.y if self.align == "center" then x = x - self.width / 2 y = y - self.height / 2 elseif self.align == "right" then x = x - self.width end local padding = 10 local textAlign = self.centeredText and "center" or "left" local textX = x + padding local textY = y + padding if self.centeredText then local _, wrappedLines = self.font:getWrap(self.visibleText, self.width - padding * 2) local numLines = wrappedLines and #wrappedLines or 1 local textHeight = self.font:getHeight() * numLines local vertExtra = math.max(0, self.height - padding * 2 - textHeight) textY = y + padding + vertExtra / 2 end local prevFont = love.graphics.getFont() love.graphics.setFont(self.font) love.graphics.setColor(1, 1, 1, 1) love.graphics.printf(self.visibleText, textX, textY, self.width - padding * 2, textAlign) love.graphics.setFont(prevFont) end function Textbox:hide() self.active = false end return Textbox