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 sub,find,reverse,rep,insert,len = string.sub,string.find,string.reverse,string.rep,table.insert,string.len
local function splitString(str, delimiter) local function splitString(str, delimiter)
@@ -16,6 +17,200 @@ local function splitString(str, delimiter)
return result return result
end 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 { return {
getTextHorizontalAlign = function(text, width, textAlign, replaceChar) getTextHorizontalAlign = function(text, width, textAlign, replaceChar)
text = sub(text, 1, width) text = sub(text, 1, width)
@@ -74,29 +269,9 @@ tableCount = function(t)
end, end,
splitString = splitString, splitString = splitString,
removeTags = removeTags,
wrapText = function(str, width) wrapText = wrapText,
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,
xmlValue = function(name, tab) xmlValue = function(name, tab)
local var local var
@@ -120,7 +295,69 @@ xmlValue = function(name, tab)
return var return var
end, 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() 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') 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 end

View File

@@ -43,26 +43,6 @@ local function stop()
end end
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) local function schedule(f)
assert(f~="function", "Schedule needs a function in order to work!") assert(f~="function", "Schedule needs a function in order to work!")
return function(...) return function(...)
@@ -160,6 +140,26 @@ local bInstance = {
end 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) local function handleSchedules(event, p1, p2, p3, p4)
if(#schedules>0)then if(#schedules>0)then
local finished = {} local finished = {}
@@ -329,6 +329,7 @@ local function createFrame(name)
end end
basalt = { basalt = {
basaltError = defaultErrorHandler,
logging = false, logging = false,
dynamicValueEvents = false, dynamicValueEvents = false,
drawFrames = drawFrames, drawFrames = drawFrames,

View File

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

View File

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

View File

@@ -99,6 +99,7 @@ return function(name, basalt)
remove = function(self) remove = function(self)
if (parent ~= nil) then if (parent ~= nil) then
parent:removeObject(self) parent:removeObject(self)
parent:removeEvents(self)
end end
self:updateDraw() self:updateDraw()
return self return self
@@ -140,6 +141,17 @@ return function(name, basalt)
return eventSystem:removeEvent(event, index) return eventSystem:removeEvent(event, index)
end, 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, ...) sendEvent = function(self, event, ...)
return eventSystem:sendEvent(event, self, event, ...) return eventSystem:sendEvent(event, self, event, ...)
end, end,

View File

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

View File

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

View File

@@ -395,17 +395,6 @@ return function(name, basalt)
end end
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) getFocusHandler = function(self)
local val = self:sendEvent("get_focus") local val = self:sendEvent("get_focus")
if(val~=nil)then return val end if(val~=nil)then return val end

View File

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