Easy Auto Wrapping Console Class

Share your scripts and packages with other Mudlet users.
Post Reply
Jor'Mox
Posts: 1142
Joined: Wed Apr 03, 2013 2:19 am

Easy Auto Wrapping Console Class

Post by Jor'Mox »

Here is a simple script that creates an autoConsole class that automatically readjusts text in the window whenever it is resized. I added all of the relevant functions that can be used to interact with a miniConsole to the class, so the returned object can use them as methods. One exception is that I haven't added compatibility for popups those functions are excluded. If I missed anything, just let me know, they are very simple to add in.
Code: [show] | [select all] lua
autoConsole = autoConsole or {}
autoConsole.list = autoConsole.list or {}
autoConsole.bufferLinks = autoConsole.bufferLinks or {}

local meta = {__index = autoConsole}

autoConsole.create = function(name, x, y, w, h)
    autoConsole.list[name] = {x = x, y = y, w = w, h = h}
    createMiniConsole(name, 0, 0, 0, 0)
    createBuffer(name.."_buffer")
    autoConsole.refresh(name)
    local self = {name = name, type = 'autoConsole', x = x, y = y, w = w, h = h}
    setmetatable(self,meta)
    return self
end

local function calcPixels(size, ref)
    local ref = ref / 100
    size = tostring(size)
    local a, sign, b = string.match(size,"(-?%d+%%?)%s*([+-])%s*(%d+%%?)")
    if not a then
        a, sign, b = string.match(size,"(-?%d+%%?)"), "+", 0
    end
    local tonum = function(x)
            if string.find(x,"%%") then
                x = math.round(x:sub(1,#x-1) * ref)
            end
            return tonumber(x)
        end
    a = tonum(a)
    b = tonum(b)
    if sign == "+" then
        return a + b
    else
        return a - b
    end
end

local function calcWindow(x,y,w,h)
    local mainw, mainh = getMainWindowSize()
    x = calcPixels(x,mainw)
    y = calcPixels(y,mainh)
    w = calcPixels(w,mainw)
    h = calcPixels(h,mainh)
    if w < 0 then
        x = mainw + w - x
        w = -w
    end
    if h < 0 then
        y = mainh + h - y
        h = -h
    end
    return x,y,w,h
end

local function cleanText(str,style)
    style = string.sub(style,1,1)
    if style == "c" then
        str = string.gsub(str,"<[%a_:]+>","")
    elseif style == "h" then
        str = string.gsub(str,    
            "%|c%w%w%w%w%w%w,[%da-fA-F][%da-fA-F][%da-fA-F][%da-fA-F][%da-fA-F][%da-fA-F]", "")
        str = string.gsub(str,"%|c%w%w%w%w%w%w","")
        str = string.gsub(str,"%|r","")
    elseif style == "d" then
        str = string.gsub(str,"<[%d,:]+>","")
        str = string.gsub(str,"<r>","")
    end
    return str
end

autoConsole.refresh = function(name)
    if type(name) == "table" then name = name.name end
    if autoConsole.list[name] then
        local buffer, list = name .. "_buffer", autoConsole.list[name]
        local x, y, w, h = calcWindow(list.x,list.y,list.w,list.h)
        resizeWindow(name,w,h)
        moveWindow(name,x,y)
        local wrap = math.floor(w / calcFontSize(name))
        setWindowWrap(name,wrap)
        clearWindow(name)
        moveCursorEnd()
        local line, moved = 0, moveCursor(buffer,0,0)
        local links = autoConsole.bufferLinks[name] or {}
        while moved do
            moveCursorEnd(name)
            local wline = getLineNumber(name)
            selectCurrentLine(buffer)
            copy(buffer)
            appendBuffer(name)
            if links[line] then
                for _,info in ipairs(links[line]) do
                    moveCursor(name,0,wline)
                    local ctext = cleanText(info[2], info[3])
                    local index, max, pos = 0, getLineCount(name) - wline, -1
                    local bpos, col, match = 0, info[1], 1
                    repeat
                        moveCursor(name, 0, wline + index)
                        local cur_line = getCurrentLine(name)
                        bpos = selectString(buffer, cur_line, 1)
                        repeat
                            pos = selectString(name, ctext, match)
                            match = match + 1
                        until(pos == -1 or pos == col - bpos)
                        if pos == -1 and bpos + #cur_line > col  then
                            selectSection(name, col - bpos, #cur_line - (col - bpos))
                            setLink(name,info[4],info[5])
                            moveCursor(name, 0, wline + index + 1)
                            cur_line = getCurrentLine(name)
                            bpos = selectString(buffer , cur_line, 1)
                            selectSection(name, 0, #ctext - (bpos - col))
                            pos = 1
                        end
                        index = index + 1
                    until(index >= max or pos ~= -1)
                    if pos ~= -1 then
                        setLink(name,info[4],info[5])
                    end
                end
            end
            line = line + 1
            moved = moveCursor(buffer,0,line)
        end
    else
        error("autoConsole.refresh: no such autoConsole window",2)
    end
end

autoConsole.refreshAll = function()
    for k,v in pairs(autoConsole.list) do
        autoConsole.refresh(k)
    end
end

autoConsole.resizeWindow = function(name, width, height)
    if type(name) == "table" then
        name.w, name.h = width, height
        name = name.name
    end
    if autoConsole.list[name] then
        autoConsole.list[name].w = width
        autoConsole.list[name].h = height
        autoConsole.refresh(name)
    else
        error("autoConsole.resizeWindow: no such autoConsole window",2)
    end
end

autoConsole.moveWindow = function(name, x, y)
    if type(name) == "table" then
        name.x, name.y = x, y
        name = name.name
    end
    if autoConsole.list[name] then
        autoConsole.list[name].x = x
        autoConsole.list[name].y = y
        autoConsole.refresh(name)
    else
        error("autoConsole.moveWindow: no such autoConsole window",2)
    end
end

autoConsole.setFontSize = function(name, size)
    if type(name) == "table" then name = name.name end
    if autoConsole.list[name] then
        setMiniConsoleFontSize(name,size)
        autoConsole.refresh(name)
    else
        error("autoConsole.setFontSize: no such autoConsole window",2)
    end
end

autoConsole.clearWindow = function(name)
    if type(name) == "table" then name = name.name end
    if autoConsole.list[name] then
        clearWindow(name)
        clearWindow(name.."_buffer")
        autoConsole.bufferLinks[name] = nil
        moveCursor(name,0,0)
        moveCursor(name.."_buffer",0,0)
    else
        error("autoConsole.setFontSize: no such autoConsole window",2)
    end
    
end

local make_link_func = function(func)
    local f = function(name, text, ...)
        if type(name) == "table" then name = name.name end
        if autoConsole.list[name] then
            local line, col = getLineNumber(name.."_buffer"), getColumnNumber(name.."_buffer")
            if string.find(func,"echo") then
                col = #getCurrentLine(name.."_buffer")
            end
            autoConsole.bufferLinks[name] = autoConsole.bufferLinks[name] or {}
            autoConsole.bufferLinks[name][line] = autoConsole.bufferLinks[name][line] or {}
            local ctext = cleanText(text,func)
            for _,info in ipairs(autoConsole.bufferLinks[name][line]) do
                if info[1] >= col then
                    info[1] = info[1] + #ctext
                end
            end
            table.insert(autoConsole.bufferLinks[name][line],
                {col, text, func, ...})
            if _G[func] then
                _G[func](name.."_buffer", text, ...)
            else
                _G[func:gsub("Link","Text")](name.."_buffer", text, ...)
            end
            autoConsole.refresh(name)
        else
            error("autoConsole."..func..": no such autoConsole window",2)
        end
    end
    return f
end

local link_funcs = {"echoLink", "insertLink", "cechoLink", "cinsertLink", "dechoLink",
    "dinsertLink", "hechoLink", "hinsertLink"}

for k,v in ipairs(link_funcs) do
    autoConsole[v] = make_link_func(v)
end

local make_func = function(func,win,buf,ref)
    local f = function(name,...)
        if type(name) == "table" then name = name.name end
        if autoConsole.list[name] then
            local result
            if win then result = {_G[func](name,...)} end
            if buf then result = {_G[func](name.."_buffer",...)} end
            if ref then autoConsole.refresh(name) end
            return unpack(result)
        else
            error("autoConsole."..func..": no such autoConsole window",2)
        end
    end
    return f
end

local dup_funcs = {'echo', 'cecho', 'hecho', 'decho', 'paste', 'appendBuffer', 'resetFormat'}

for k,v in ipairs(dup_funcs) do
    autoConsole[v] = make_func(v,true,true,true)
end

local ref_funcs = {'insertText', 'cinsertText', 'bg', 'replace', 'deleteLine', 'fg',
    'setBgColor', 'setFgColor', 'setStrikeOut', 'setHexFgColor', 'setHexBgColor',
    'setBold', 'setUnderline', 'setItalics',}

for k,v in ipairs(ref_funcs) do
    autoConsole[v] = make_func(v,false,true,true)
end

local buf_funcs = {'moveCursor', 'selectSection', 'selectString', 'selectCurrentLine',
    'copy', 'getLineNumber', 'getColumNumber', 'getFont', 'deselect', 'getBgColor',
    'getFgColor', 'getLastLineNumber', 'moveCursorEnd', 'getLineCount', 'getCurrentLine'}

for k,v in ipairs(buf_funcs) do
    autoConsole[v] = make_func(v,false,true,false)
end

local win_funcs = {'hideWindow', 'showWindow', 'setTextFormat', 'getFont', 'getFontSize',
    'enableScrollBar', 'disableScrollBar', 'lowerWindow', 'raiseWindow', 'getRowCount',
    'getColumnCount', 'setBackgroundColor', 'setBackgroundImage',}
    
for k,v in ipairs(win_funcs) do
    autoConsole[v] = make_func(v,true,false,false)
end

registerAnonymousEventHandler("sysWindowResizeEvent", "autoConsole.refreshAll")
To use, call autoConsole.create with all the arguments you would give to createMiniConsole, and optionally save the returned value in a variable for easy manipulation. Example:
Code: [show] | [select all] lua
local console = autoConsole.create("myConsole",0,0,300,250)
console:echo("This is a test, and I'm writing a lot just so the line is long enough to wrap.")
console:resizeWindow(200,250)
console:setFontSize(14)
console:clearWindow()
Edit: Added Geyser-like sizing, positioning, and auto-handling when the main window is resized. So now you can set size and positioning as percentages or flat numbers (or also a combo of both, like "10% + 15"), and the window maintains the proper size and position as the main window is resized automatically, like would happen for a Geyser miniConsole.

Edit: Added the capability to use insertLink and echoLink and their variations (including cinsertLink and the like, which don't exist normally) with the console windows. As a minor "issue", when using echoLink and insertLink, they will default to a background color of black, even if the background color of the console you see is a different color, this is because they don't apply formatting, and the text itself is drawn from a buffer, which can't have its background color set.

reldi
Posts: 1
Joined: Fri Nov 02, 2018 4:56 pm

Re: Easy Auto Wrapping Console Class

Post by reldi »

Has anyone tried to implement this into Guyser MiniConsoles?

Jor'Mox
Posts: 1142
Joined: Wed Apr 03, 2013 2:19 am

Re: Easy Auto Wrapping Console Class

Post by Jor'Mox »

Not to my knowledge, no. For a lot of use cases of miniConsoles, this is excessive, so it would definitely be something you would want as optional, not a built in part of normal function.

Post Reply