Skip to content

Instantly share code, notes, and snippets.

@Mijyuoon
Created December 31, 2019 17:45
Show Gist options
  • Save Mijyuoon/c7247025df71bed381c137ddaeac4078 to your computer and use it in GitHub Desktop.
Save Mijyuoon/c7247025df71bed381c137ddaeac4078 to your computer and use it in GitHub Desktop.
GMod text wrapper nonsense
local mt_wraptxt = {}
mt_wraptxt.__index = mt_wraptxt
scr.META_WordWrapper = mt_wraptxt
local worditer
local CK_NONE = 0
local CK_SPACE = 1
local CK_TAB = 3
local CK_LINE = 2
do ---- Word iterator ------------------
local whitespace = {
[0x0009] = CK_TAB,
[0x000A] = CK_LINE,
[0x000B] = CK_SPACE,
[0x000C] = CK_SPACE,
[0x000D] = CK_LINE,
[0x0020] = CK_SPACE,
}
function worditer(str)
local iterator = utf8.codes(str)
local ltPos, ltChar = iterator()
local ltKind = whitespace[ltChar]
local rtPos, rtChar, rtKind
local wordPos, endPos = 1, 1
return function()
while true do
if not wordPos then break end
local word, wType = nil
rtPos, rtChar = iterator()
rtKind = whitespace[rtChar]
if not rtPos or rtKind ~= ltKind then
endPos = (rtPos or 0) - 1
word = str:sub(wordPos, endPos)
wType = ltKind or CK_NONE
wordPos = rtPos
end
ltPos, ltChar, ltKind = rtPos, rtChar, rtKind
if word then return word, wType end
end
return nil
end
end
end ------------------------------------
function mt_wraptxt:PerformUpdate()
local wrapW = self._wrapwidth
if wrapW < 1 then return end
local lines, maxW = {}, 0
local curW, curBuf = 0, ""
for word, wType in worditer(self._text) do
if wType == CK_NONE then
local w0 = scr.TextSize(word, self._font)
if curW + w0 <= wrapW then
curBuf = curBuf .. word
curW = curW + w0
elseif w0 > wrapW then
for j, ci in utf8.codes(word) do
local ch = utf8.char(ci)
local nxtBuf = curBuf .. ch
local w1 = scr.TextSize(nxtBuf, self._font)
if w1 > wrapW then
lines[#lines+1] = curBuf
maxW = math.max(maxW, curW)
local csz = scr.TextSize(ch, self._font)
curW, curBuf = csz, ch
else
curW, curBuf = w1, nxtBuf
end
end
else
lines[#lines+1] = curBuf
maxW = math.max(maxW, curW)
curW, curBuf = w0, word
end
elseif wType == CK_TAB
or wType == CK_SPACE then
if wType == CK_TAB then
word = string.rep(" ", #word * self._tabwidth)
end
local w0 = scr.TextSize(word, self._font)
if curW + w0 <= wrapW then
curBuf = curBuf .. word
end
curW = curW + w0
elseif wType == CK_LINE then
lines[#lines+1] = curBuf
maxW = math.max(maxW, curW)
curW, curBuf = 0, ""
for i = 1, #word-1 do
lines[#lines+1] = false
end
end
end
if #curBuf > 0 then
lines[#lines+1] = curBuf
maxW = math.max(maxW, curW)
end
local _, hgt = scr.TextSize(" ", self._font)
self._size[1] = math.min(wrapW, maxW)
self._size[2] = #lines * hgt
self._lines = lines
end
function mt_wraptxt:QueryUpdate()
if self._isdirty then
self._isdirty = nil
self:PerformUpdate()
end
end
function mt_wraptxt:GetText()
return self._text
end
function mt_wraptxt:SetText(txt)
if self._text ~= txt then
self._isdirty = true
end
self._text = txt
end
function mt_wraptxt:GetFont()
return self._font
end
function mt_wraptxt:SetFont(fnt)
if self._font ~= fnt then
self._isdirty = true
end
self._font = fnt
end
function mt_wraptxt:GetSize()
return unpack(self._size)
end
function mt_wraptxt:GetWrapWidth()
return self._wrapwidth
end
function mt_wraptxt:SetWrapWidth(w)
if self._wrapwidth ~= w then
self._isdirty = true
end
self._wrapwidth = w
end
function mt_wraptxt:Clear()
self._text = ""
self._lines = {}
end
function mt_wraptxt:Render(x,y,col,xal)
self:QueryUpdate()
local count = #self._lines
if count < 1 then return end
local lineHgt = self._size[2] / count
for i = 1, count do
local line = self._lines[i]
if not line then continue end
scr.DrawText(x, y+lineHgt*(i-1), line, xal or -1, -1, col, self._font)
end
end
function scr.WordWrapper(fnt, txt)
return setmetatable({
_lines = {},
_size = {0, 0},
_wrapwidth = 0,
_tabwidth = 4,
_text = txt or "",
_font = fnt or "Default",
}, mt_wraptxt)
end
----------------------------------
-- Extra shit used by the above --
----------------------------------
local halign_map = {
[-1] = TEXT_ALIGN_LEFT,
[ 0] = TEXT_ALIGN_CENTER,
[ 1] = TEXT_ALIGN_RIGHT,
}
local valign_map = {
[-1] = TEXT_ALIGN_TOP,
[ 0] = TEXT_ALIGN_CENTER,
[ 1] = TEXT_ALIGN_BOTTOM,
}
function scr.DrawText(x,y,text,xal,yal,col,font)
col = col or color_white
font = font or "Default"
xal = halign_map[xal]
yal = valign_map[yal]
draw.SimpleText(text, font, x, y, col, xal, yal)
end
function scr.TextSize(text,font)
surface.SetFont(font)
return surface.GetTextSize(text)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment