Small Fixes

- added Label RichText System - experimental
- moved eventhandlers to object
- Fixed still listening to incomming events when removing a object
- Fixed not passing args when creating a event via xml, args are now always stored inside a table called event
This commit is contained in:
Robert Jelic
2023-05-07 15:17:27 +02:00
parent f3f8105459
commit c9a74dfbd4
9 changed files with 356 additions and 104 deletions

View File

@@ -1,3 +1,4 @@
local tHex = require("tHex")
local sub,find,reverse,rep,insert,len = string.sub,string.find,string.reverse,string.rep,table.insert,string.len
local function splitString(str, delimiter)
@@ -16,6 +17,200 @@ local function splitString(str, delimiter)
return result
end
local function removeTags(input)
return input:gsub("{[^}]+}", "")
end
local function wrapText(str, width)
str = removeTags(str)
if(str=="")or(width==0)then
return {""}
end
local uniqueLines = splitString(str, "\n")
local result = {}
for k, v in pairs(uniqueLines) do
if #v == 0 then
table.insert(result, "")
else
while #v > width do
local last_space = width
for i = width, 1, -1 do
if sub(v, i, i) == " " then
last_space = i
break
end
end
if last_space == width then
local line = sub(v, 1, last_space - 1) .. "-"
table.insert(result, line)
v = sub(v, last_space)
else
local line = sub(v, 1, last_space - 1)
table.insert(result, line)
v = sub(v, last_space + 1)
end
if #v <= width then
break
end
end
if #v > 0 then
table.insert(result, v)
end
end
end
return result
end
--- Coonverts a string with special tags to a table with colors and text
-- @param input The string to convert
-- @return A table with the following structure: { {text = "Hello", color = colors.red}, {text = "World", color = colors.blue} }
local function convertRichText(input)
local parsedResult = {}
local currentPosition = 1
local rawPosition = 1
while currentPosition <= #input do
local closestColor, closestBgColor
local color, bgColor
local colorEnd, bgColorEnd
for colorName, _ in pairs(colors) do
local fgPattern = "{fg:" .. colorName.."}"
local bgColorPattern = "{bg:" .. colorName.."}"
local colorStart, colorEndCandidate = input:find(fgPattern, currentPosition)
local bgColorStart, bgColorEndCandidate = input:find(bgColorPattern, currentPosition)
if colorStart and (not closestColor or colorStart < closestColor) then
closestColor = colorStart
color = colorName
colorEnd = colorEndCandidate
end
if bgColorStart and (not closestBgColor or bgColorStart < closestBgColor) then
closestBgColor = bgColorStart
bgColor = colorName
bgColorEnd = bgColorEndCandidate
end
end
local nextPosition
if closestColor and (not closestBgColor or closestColor < closestBgColor) then
nextPosition = closestColor
elseif closestBgColor then
nextPosition = closestBgColor
else
nextPosition = #input + 1
end
local text = input:sub(currentPosition, nextPosition - 1)
if #text > 0 then
table.insert(parsedResult, {
color = nil,
bgColor = nil,
text = text,
position = rawPosition
})
rawPosition = rawPosition + #text
currentPosition = currentPosition + #text
end
if closestColor and (not closestBgColor or closestColor < closestBgColor) then
table.insert(parsedResult, {
color = color,
bgColor = nil,
text = "",
position = rawPosition,
})
currentPosition = colorEnd + 1
elseif closestBgColor then
table.insert(parsedResult, {
color = nil,
bgColor = bgColor,
text = "",
position = rawPosition,
})
currentPosition = bgColorEnd + 1
else
break
end
end
return parsedResult
end
--- Wrapts text with special color tags, like {fg:red} or {bg:blue} to multiple lines
--- @param text string Text to wrap
--- @param width number Width of the line
--- @return table Table of lines
local function wrapRichText(text, width)
local colorData = convertRichText(text)
local formattedLines = {}
local x, y = 1, 1
local currentColor, currentBgColor
local function addFormattedEntry(entry)
table.insert(formattedLines, {
x = x,
y = y,
text = entry.text,
color = entry.color or currentColor,
bgColor = entry.bgColor or currentBgColor
})
end
for _, entry in ipairs(colorData) do
if entry.color then
currentColor = entry.color
elseif entry.bgColor then
currentBgColor = entry.bgColor
else
local words = splitString(entry.text, " ")
for i, word in ipairs(words) do
local wordLength = #word
if i > 1 then
if x + 1 + wordLength <= width then
addFormattedEntry({ text = " " })
x = x + 1
else
x = 1
y = y + 1
end
end
while wordLength > 0 do
local line = word:sub(1, width - x + 1)
word = word:sub(width - x + 2)
wordLength = #word
addFormattedEntry({ text = line })
if wordLength > 0 then
x = 1
y = y + 1
else
x = x + #line
end
end
end
end
if x > width then
x = 1
y = y + 1
end
end
return formattedLines
end
return {
getTextHorizontalAlign = function(text, width, textAlign, replaceChar)
text = sub(text, 1, width)
@@ -74,29 +269,9 @@ tableCount = function(t)
end,
splitString = splitString,
removeTags = removeTags,
wrapText = function(str, width)
local uniqueLines = splitString(str, "\n")
local result = {}
for k,v in pairs(uniqueLines)do
if(#v==0)then table.insert(result, "") end
while #v > width do
local last_space = find(reverse(sub(v, 1, width)), " ")
if not last_space then
last_space = width
else
last_space = width - last_space --+ 1
end
local line = sub(v, 1, last_space)
table.insert(result, line)
v = sub(v, last_space + 1)
end
if #v > 0 then
table.insert(result, v)
end
end
return result
end,
wrapText = wrapText,
xmlValue = function(name, tab)
local var
@@ -120,7 +295,69 @@ xmlValue = function(name, tab)
return var
end,
convertRichText = convertRichText,
--- Writes text with special color tags
--- @param obj object The object to write to
--- @param x number X-Position
--- @param y number Y-Position
--- @param text string The text to write
writeRichText = function(obj, x, y, text)
local richText = convertRichText(text)
if(#richText==0)then
obj:addText(x, y, text)
return
end
local defaultFG, defaultBG = obj:getForeground(), obj:getBackground()
for _,v in pairs(richText)do
obj:addText(x+v.position-1, y, v.text)
if(v.color~=nil)then
obj:addFG(x+v.position-1, y, tHex[colors[v.color] ]:rep(#v.text))
defaultFG = colors[v.color]
else
obj:addFG(x+v.position-1, y, tHex[defaultFG]:rep(#v.text))
end
if(v.bgColor~=nil)then
obj:addBG(x+v.position-1, y, tHex[colors[v.bgColor] ]:rep(#v.text))
defaultBG = colors[v.bgColor]
else
if(defaultBG~=false)then
obj:addBG(x+v.position-1, y, tHex[defaultBG]:rep(#v.text))
end
end
end
end,
wrapRichText = wrapRichText,
--- Writes wrapped Text with special tags.
--- @param obj object The object to write to
--- @param x number X-Position
--- @param y number Y-Position
--- @param text string Text
--- @param width number Width
--- @param height number Height
writeWrappedText = function(obj, x, y, text, width, height)
local wrapped = wrapRichText(text, width)
for k,v in pairs(wrapped)do
if(v.y>height)then
break
end
if(v.text~=nil)then
obj:addText(x+v.x-1, y+v.y-1, v.text)
end
if(v.color~=nil)then
obj:addFG(x+v.x-1, y+v.y-1, tHex[colors[v.color] ]:rep(#v.text))
end
if(v.bgColor~=nil)then
obj:addBG(x+v.x-1, y+v.y-1, tHex[colors[v.bgColor] ]:rep(#v.text))
end
end
end,
--- Returns a random UUID.
--- @return string UUID.
uuid = function()
return string.gsub(string.format('%x-%x-%x-%x-%x', math.random(0, 0xffff), math.random(0, 0xffff), math.random(0, 0xffff), math.random(0, 0x0fff) + 0x4000, math.random(0, 0x3fff) + 0x8000), ' ', '0')
end

View File

@@ -43,26 +43,6 @@ local function stop()
end
end
function basalt.basaltError(errMsg)
baseTerm.clear()
baseTerm.setBackgroundColor(colors.black)
baseTerm.setTextColor(colors.red)
local w,h = baseTerm.getSize()
if(basalt.logging)then
log(errMsg, "Error")
end
local text = wrapText("Basalt error: "..errMsg, w)
local yPos = 1
for k,v in pairs(text)do
baseTerm.setCursorPos(1,yPos)
baseTerm.write(v)
yPos = yPos + 1
end
baseTerm.setCursorPos(1,yPos+1)
updaterActive = false
end
local function schedule(f)
assert(f~="function", "Schedule needs a function in order to work!")
return function(...)
@@ -160,6 +140,26 @@ local bInstance = {
end
}
local function defaultErrorHandler(errMsg)
baseTerm.clear()
baseTerm.setBackgroundColor(colors.black)
baseTerm.setTextColor(colors.red)
local w,h = baseTerm.getSize()
if(basalt.logging)then
log(errMsg, "Error")
end
local text = wrapText("Basalt error: "..errMsg, w)
local yPos = 1
for k,v in pairs(text)do
baseTerm.setCursorPos(1,yPos)
baseTerm.write(v)
yPos = yPos + 1
end
baseTerm.setCursorPos(1,yPos+1)
updaterActive = false
end
local function handleSchedules(event, p1, p2, p3, p4)
if(#schedules>0)then
local finished = {}
@@ -329,6 +329,7 @@ local function createFrame(name)
end
basalt = {
basaltError = defaultErrorHandler,
logging = false,
dynamicValueEvents = false,
drawFrames = drawFrames,

View File

@@ -10,7 +10,6 @@ return function(name, basalt)
local events = {}
local container = {}
local activeEvents = {}
local focusedObject
local sorted = true
@@ -69,14 +68,14 @@ return function(name, basalt)
local function updateZIndex(self, element, newZ)
objId = objId + 1
evId = evId + 1
for k,v in pairs(elements)do
for _,v in pairs(elements)do
if(v.element==element)then
v.zIndex = newZ
v.objId = objId
break
end
end
for k,v in pairs(events)do
for _,v in pairs(events)do
for a,b in pairs(v)do
if(b.element==element)then
b.zIndex = newZ
@@ -109,7 +108,6 @@ return function(name, basalt)
end
end
if(tableCount(events[a])<=0)then
activeEvents[a] = false
if(parent~=nil)then
parent:removeEvent(a, self)
end
@@ -143,7 +141,6 @@ return function(name, basalt)
end
local function removeEvent(self, event, element)
local parent = self:getParent()
if(events[event]~=nil)then
for a, b in pairs(events[event]) do
if(b.element == element)then
@@ -288,11 +285,11 @@ return function(name, basalt)
getEvent = getEvent,
addEvent = addEvent,
removeEvent = removeEvent,
removeEvents = removeEvents,
updateZIndex = updateZIndex,
listenEvent = function(self, event, active)
base.listenEvent(self, event, active)
activeEvents[event] = active~=nil and active or true
if(events[event]==nil)then events[event] = {} end
return self
end,
@@ -348,8 +345,10 @@ return function(name, basalt)
if(self.getOffset~=nil)then
xO, yO = self:getOffset()
end
if(obj.element.getIgnoreOffset())then
xO, yO = 0, 0
if(obj.element.getIgnoreOffset~=nil)then
if(obj.element.getIgnoreOffset())then
xO, yO = 0, 0
end
end
if (obj.element[v[1]](obj.element, btn, x+xO, y+yO, ...)) then
return true

View File

@@ -1,5 +1,6 @@
local utils = require("utils")
local wrapText = utils.wrapText
local writeWrappedText = utils.writeWrappedText
local tHex = require("tHex")
return function(name, basalt)
@@ -8,92 +9,97 @@ return function(name, basalt)
local objectType = "Label"
base:setZIndex(3)
base:setSize(5, 1)
base:setBackground(false)
local autoSize = true
local fgColChanged,bgColChanged = false,false
local text, textAlign = "Label", "left"
local object = {
--- Returns the object type.
--- @return string
getType = function(self)
return objectType
end,
--- Returns the label's base object.
--- @return object
getBase = function(self)
return base
end,
end,
--- Changes the label's text.
--- @param newText string The new text of the label.
--- @return object
setText = function(self, newText)
text = tostring(newText)
if(autoSize)then
self:setSize(#text, 1)
local t = wrapText(text, #text)
local newW, newH = 1,1
for k,v in pairs(t)do
newH = newH+1
newW = math.max(newW, v:len())
end
self:setSize(newW, newH)
autoSize = true
end
self:updateDraw()
return self
end,
--- Returns the label's autoSize property.
--- @return boolean
getAutoSize = function(self)
return autoSize
end,
--- Sets the label's autoSize property.
--- @param bool boolean The new value of the autoSize property.
--- @return object
setAutoSize = function(self, bool)
autoSize = bool
return self
end,
--- Returns the label's text.
--- @return string
getText = function(self)
return text
end,
setBackground = function(self, col)
base.setBackground(self, col)
bgColChanged = true
return self
end,
setForeground = function(self, col)
base.setForeground(self, col)
fgColChanged = true
return self
end,
setSize = function(self, ...)
base.setSize(self, ...)
--- Sets the size of the label.
--- @param width number The width of the label.
--- @param height number The height of the label.
--- @return object
setSize = function(self, width, height)
base.setSize(self, width, height)
autoSize = false
return self
end,
--- Sets the text alignment of the label.
--- @param align string The alignment of the text. Can be "left", "center", or "right".
--- @return object
setTextAlign = function(self, align)
textAlign = align or textAlign
return self;
end,
--- Queues a new draw function to be called when the object is drawn.
draw = function(self)
base.draw(self)
self:addDraw("label", function()
local parent = self:getParent()
local obx, oby = self:getPosition()
local w,h = self:getSize()
local bgCol,fgCol = self:getBackground(), self:getForeground()
if not(autoSize)then
local text = wrapText(text, w)
for k,v in pairs(text)do
if(k<=h)then
local align = textAlign=="center" and math.floor(w/2-v:len()/2+0.5) or textAlign=="right" and w-(v:len()-1) or 1
self:addText(align, k, v)
end
end
else
self:addText(1, 1, text:sub(1,w))
end
local w, h = self:getSize()
local align = textAlign=="center" and math.floor(w/2-text:len()/2+0.5) or textAlign=="right" and w-(text:len()-1) or 1
writeWrappedText(self, align, 1, text, w, h)
end)
end,
--- Initializes the label.
init = function(self)
base.init(self)
local parent = self:getParent()
self:setForeground(parent:getForeground())
self:setBackground(parent:getBackground())
end
}

View File

@@ -99,6 +99,7 @@ return function(name, basalt)
remove = function(self)
if (parent ~= nil) then
parent:removeObject(self)
parent:removeEvents(self)
end
self:updateDraw()
return self
@@ -140,6 +141,17 @@ return function(name, basalt)
return eventSystem:removeEvent(event, index)
end,
eventHandler = function(self, event, ...)
local val = self:sendEvent("other_event", event, ...)
if(val~=nil)then return val end
end,
customEventHandler = function(self, event, ...)
local val = self:sendEvent("custom_event", event, ...)
if(val~=nil)then return val end
return true
end,
sendEvent = function(self, event, ...)
return eventSystem:sendEvent(event, self, event, ...)
end,

View File

@@ -85,6 +85,7 @@ return function(name, basalt)
end,
eventHandler = function(self, event, ...)
base.eventHandler(self, event, ...)
if (isActive) then
if (coroutine.status(cRoutine) == "suspended") then
if(filter~=nil)then

View File

@@ -48,7 +48,8 @@ return function(name, basalt)
return self
end,
eventHandler = function(self, event, tObj)
eventHandler = function(self, event, ...)
base.eventHandler(self, event, ...)
if event == "timer" and tObj == timerObj and timerIsActive then
self:sendEvent("timed_event")
if (repeats >= 1) then

View File

@@ -395,17 +395,6 @@ return function(name, basalt)
end
end,
eventHandler = function(self, event, ...)
local val = self:sendEvent("other_event", ...)
if(val~=nil)then return val end
end,
customEventHandler = function(self, event, ...)
local val = self:sendEvent("custom_event", ...)
if(val~=nil)then return val end
return true
end,
getFocusHandler = function(self)
local val = self:sendEvent("get_focus")
if(val~=nil)then return val end

View File

@@ -159,11 +159,15 @@ local function executeScript(scripts)
end
local function registerFunctionEvent(self, data, event, scripts)
local eventEnv = scripts.env
if(data:sub(1,1)=="$")then
local data = data:sub(2)
event(self, self:getBasalt():getVariable(data))
event(self, self:getBasalt().getVariable(data))
else
event(self, load(data, nil, "t", scripts.env))
event(self, function(...)
eventEnv.event = {...}
load(data, nil, "t", eventEnv)()
end)
end
end
@@ -181,8 +185,8 @@ return {
if(xmlValue("height", data)~=nil)then h = xmlValue("height", data) end
if(xmlValue("background", data)~=nil)then self:setBackground(colors[xmlValue("background", data)]) end
if(xmlValue("script", data)~=nil)then
if(xmlValue("script", data)~=nil)then
if(scripts[1]==nil)then
scripts[1] = {}
end
@@ -248,7 +252,7 @@ return {
lastXMLReferences = {}
base.setValuesByXMLData(self, data, scripts)
local xOffset, yOffset = self:getOffset()
if(xmlValue("layout", data)~=nil)then self:addLayout(xmlValue("layout", data)) end
if(xmlValue("layout", data)~=nil)then self:loadLayout(xmlValue("layout", data)) end
if(xmlValue("xOffset", data)~=nil)then xOffset = xmlValue("xOffset", data) end
self:setOffset(xOffset, yOffset)
@@ -316,7 +320,7 @@ return {
setValuesByXMLData = function(self, data, scripts)
base.setValuesByXMLData(self, data, scripts)
local xOffset, yOffset = self:getOffset()
if(xmlValue("layout", data)~=nil)then self:addLayout(xmlValue("layout", data)) end
if(xmlValue("layout", data)~=nil)then self:loadLayout(xmlValue("layout", data)) end
if(xmlValue("xOffset", data)~=nil)then xOffset = xmlValue("xOffset", data) end
if(xmlValue("yOffset", data)~=nil)then yOffset = xmlValue("yOffset", data) end
self:setOffset(xOffset, yOffset)
@@ -580,10 +584,12 @@ return {
end,
Thread = function(base, basalt)
local object = {
local object = {
setValuesByXMLData = function(self, data, scripts)
base.setValuesByXMLData(self, data, scripts)
if(xmlValue("start", data)~=nil)then self:start(load(xmlValue("start", data), nil, "t", scripts.env)) end
if(xmlValue("start", data)~=nil)then
local f = load(xmlValue("start", data), nil, "t", scripts.env)
self:start(f)
end
return self
end,
}