Lots of shit

This commit is contained in:
2025-06-02 10:23:40 +03:00
parent f1b31c5c3e
commit 42b7d798e1
68 changed files with 11580 additions and 516 deletions

View File

@@ -0,0 +1,231 @@
local drawSystem = require("basaltDraw")
local utils = require("utils")
local max,min,sub,rep = math.max,math.min,string.sub,string.rep
return function(name, basalt)
local base = basalt.getObject("Container")(name, basalt)
local objectType = "BaseFrame"
local xOffset, yOffset = 0, 0
local colorTheme = {}
local updateRender = true
local termObject = basalt.getTerm()
local basaltDraw = drawSystem(termObject)
local xCursor, yCursor, cursorBlink, cursorColor = 1, 1, false, colors.white
local object = {
getType = function()
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
getBase = function(self)
return base
end,
getOffset = function(self)
return xOffset, yOffset
end,
setOffset = function(self, xOff, yOff)
xOffset = xOff or xOffset
yOffset = yOff or yOffset
self:updateDraw()
return self
end,
getXOffset = function(self)
return xOffset
end,
setXOffset = function(self, newXOffset)
return self:setOffset(newXOffset, nil)
end,
getYOffset = function(self)
return yOffset
end,
setYOffset = function(self, newYOffset)
return self:setOffset(nil, newYOffset)
end,
setPalette = function(self, col, ...)
if(self==basalt.getActiveFrame())then
if(type(col)=="string")then
colorTheme[col] = ...
termObject.setPaletteColor(type(col)=="number" and col or colors[col], ...)
elseif(type(col)=="table")then
for k,v in pairs(col)do
colorTheme[k] = v
if(type(v)=="number")then
termObject.setPaletteColor(type(k)=="number" and k or colors[k], v)
else
local r,g,b = table.unpack(v)
termObject.setPaletteColor(type(k)=="number" and k or colors[k], r,g,b)
end
end
end
end
return self
end,
setSize = function(self, ...)
base.setSize(self, ...)
basaltDraw = drawSystem(termObject)
return self
end,
getSize = function()
return termObject.getSize()
end,
getWidth = function(self)
return ({termObject.getSize()})[1]
end,
getHeight = function(self)
return ({termObject.getSize()})[2]
end,
show = function(self)
base.show(self)
basalt.setActiveFrame(self)
for k,v in pairs(colors)do
if(type(v)=="number")then
termObject.setPaletteColor(v, colors.packRGB(term.nativePaletteColor((v))))
end
end
for k,v in pairs(colorTheme)do
if(type(v)=="number")then
termObject.setPaletteColor(type(k)=="number" and k or colors[k], v)
else
local r,g,b = table.unpack(v)
termObject.setPaletteColor(type(k)=="number" and k or colors[k], r,g,b)
end
end
basalt.setMainFrame(self)
return self
end,
render = function(self)
if(base.render~=nil)then
if(self:isVisible())then
if(updateRender)then
base.render(self)
local children = self:getChildren()
for _, child in ipairs(children) do
if (child.element.render ~= nil) then
child.element:render()
end
end
updateRender = false
end
end
end
end,
updateDraw = function(self)
updateRender = true
return self
end,
eventHandler = function(self, event, ...)
base.eventHandler(self, event, ...)
if(event=="term_resize")then
self:setSize(termObject.getSize())
end
end,
updateTerm = function(self)
if(basaltDraw~=nil)then
basaltDraw.update()
end
end,
setTerm = function(self, newTerm)
termObject = newTerm
if(newTerm==nil)then
basaltDraw = nil
else
basaltDraw = drawSystem(termObject)
end
return self
end,
getTerm = function()
return termObject
end,
blit = function (self, x, y, t, f, b)
local obx, oby = self:getPosition()
local w, h = self:getSize()
if y >= 1 and y <= h then
local t_visible = sub(t, max(1 - x + 1, 1), max(w - x + 1, 1))
local f_visible = sub(f, max(1 - x + 1, 1), max(w - x + 1, 1))
local b_visible = sub(b, max(1 - x + 1, 1), max(w - x + 1, 1))
basaltDraw.blit(max(x + (obx - 1), obx), oby + y - 1, t_visible, f_visible, b_visible)
end
end,
setCursor = function(self, _blink, _xCursor, _yCursor, color)
local obx, oby = self:getAbsolutePosition()
local xO, yO = self:getOffset()
cursorBlink = _blink or false
if (_xCursor ~= nil) then
xCursor = obx + _xCursor - 1 - xO
end
if (_yCursor ~= nil) then
yCursor = oby + _yCursor - 1 - yO
end
cursorColor = color or cursorColor
if (cursorBlink) then
termObject.setTextColor(cursorColor)
termObject.setCursorPos(xCursor, yCursor)
termObject.setCursorBlink(cursorBlink)
else
termObject.setCursorBlink(false)
end
return self
end,
}
for k,v in pairs({mouse_click={"mouseHandler", true},mouse_up={"mouseUpHandler", false},mouse_drag={"dragHandler", false},mouse_scroll={"scrollHandler", true},mouse_hover={"hoverHandler", false}})do
object[v[1]] = function(self, btn, x, y, ...)
if(base[v[1]](self, btn, x, y, ...))then
basalt.setActiveFrame(self)
end
end
end
for k,v in pairs({"drawBackgroundBox", "drawForegroundBox", "drawTextBox"})do
object[v] = function(self, x, y, width, height, symbol)
local obx, oby = self:getPosition()
local w, h = self:getSize()
height = (y < 1 and (height + y > self:getHeight() and self:getHeight() or height + y - 1) or (height + y > self:getHeight() and self:getHeight() - y + 1 or height))
width = (x < 1 and (width + x > self:getWidth() and self:getWidth() or width + x - 1) or (width + x > self:getWidth() and self:getWidth() - x + 1 or width))
basaltDraw[v](max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, symbol)
end
end
for k,v in pairs({"setBG", "setFG", "setText"}) do
object[v] = function(self, x, y, str)
local obx, oby = self:getPosition()
local w, h = self:getSize()
if (y >= 1) and (y <= h) then
basaltDraw[v](max(x + (obx - 1), obx), oby + y - 1, sub(str, max(1 - x + 1, 1), max(w - x + 1,1)))
end
end
end
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,77 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
-- Button
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Button"
local textHorizontalAlign = "center"
local textVerticalAlign = "center"
local text = "Button"
base:setSize(12, 3)
base:setZIndex(5)
local object = {
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
getBase = function(self)
return base
end,
getHorizontalAlign = function(self)
return textHorizontalAlign
end,
setHorizontalAlign = function(self, pos)
textHorizontalAlign = pos
self:updateDraw()
return self
end,
getVerticalAlign = function(self)
return textVerticalAlign
end,
setVerticalAlign = function(self, pos)
textVerticalAlign = pos
self:updateDraw()
return self
end,
getText = function(self)
return text
end,
setText = function(self, newText)
text = newText
self:updateDraw()
return self
end,
draw = function(self)
base.draw(self)
self:addDraw("button", function()
local w,h = self:getSize()
local verticalAlign = utils.getTextVerticalAlign(h, textVerticalAlign)
local xOffset
if(textHorizontalAlign=="center")then
xOffset = math.floor((w - text:len()) / 2)
elseif(textHorizontalAlign=="right")then
xOffset = w - text:len()
end
self:addText(xOffset + 1, verticalAlign, text)
self:addFG(xOffset + 1, verticalAlign, tHex[self:getForeground() or colors.white]:rep(text:len()))
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,40 @@
return function(name, basalt)
local base = basalt.getObject("VisualObject")(name, basalt)
-- Base object
local objectType = "ChangeableObject"
local value
local object = {
setValue = function(self, _value, valueChangedHandler)
if (value ~= _value) then
value = _value
self:updateDraw()
if(valueChangedHandler~=false)then
self:valueChangedHandler()
end
end
return self
end,
getValue = function(self)
return value
end,
onChange = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("value_changed", v)
end
end
return self
end,
valueChangedHandler = function(self)
self:sendEvent("value_changed", value)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,114 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
-- Checkbox
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Checkbox"
base:setZIndex(5)
base:setValue(false)
base:setSize(1, 1)
local symbol,inactiveSymbol,text,textPos = "\42"," ","","right"
local object = {
load = function(self)
self:listenEvent("mouse_click", self)
self:listenEvent("mouse_up", self)
end,
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
setSymbol = function(self, sym, inactive)
symbol = sym or symbol
inactiveSymbol = inactive or inactiveSymbol
self:updateDraw()
return self
end,
setActiveSymbol = function(self, sym)
return self:setSymbol(sym, nil)
end,
setInactiveSymbol = function(self, inactive)
return self:setSymbol(nil, inactive)
end,
getSymbol = function(self)
return symbol, inactiveSymbol
end,
getActiveSymbol = function(self)
return symbol
end,
getInactiveSymbol = function(self)
return inactiveSymbol
end,
setText = function(self, _text)
text = _text
return self
end,
getText = function(self)
return text
end,
setTextPosition = function(self, pos)
textPos = pos or textPos
return self
end,
getTextPosition = function(self)
return textPos
end,
setChecked = base.setValue,
getChecked = base.getValue,
mouseHandler = function(self, button, x, y)
if (base.mouseHandler(self, button, x, y)) then
if(button == 1)then
if (self:getValue() ~= true) and (self:getValue() ~= false) then
self:setValue(false)
else
self:setValue(not self:getValue())
end
self:updateDraw()
return true
end
end
return false
end,
draw = function(self)
base.draw(self)
self:addDraw("checkbox", function()
local obx, oby = self:getPosition()
local w,h = self:getSize()
local verticalAlign = utils.getTextVerticalAlign(h, "center")
local bg,fg = self:getBackground(), self:getForeground()
if (self:getValue()) then
self:addBlit(1, verticalAlign, utils.getTextHorizontalAlign(symbol, w, "center"), tHex[fg], tHex[bg])
else
self:addBlit(1, verticalAlign, utils.getTextHorizontalAlign(inactiveSymbol, w, "center"), tHex[fg], tHex[bg])
end
if(text~="")then
local align = textPos=="left" and -text:len() or 3
self:addText(align, verticalAlign, text)
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,429 @@
local utils = require("utils")
local tableCount = utils.tableCount
return function(name, basalt)
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Container"
local children = {}
local events = {}
local container = {}
local focusedChild
local sorted = true
local objId, evId = 0, 0
local objSort = function(a, b)
if a.zIndex == b.zIndex then
return a.objId < b.objId
else
return a.zIndex < b.zIndex
end
end
local evSort = function(a, b)
if a.zIndex == b.zIndex then
return a.evId > b.evId
else
return a.zIndex > b.zIndex
end
end
local function getChildren(self)
self:sortChildren()
return children
end
local function getChild(self, name)
if (type(name)=="table") then
name = name:getName()
end
for _, v in ipairs(children) do
if v.element:getName() == name then
return v.element
end
end
end
local function getDeepChild(self, name)
local maybeChild = getChild(name)
if (maybeChild ~= nil) then
return maybeChild
end
for _, child in ipairs(children) do
if (child:getType() == "Container") then
local maybeDeepChild = child:getDeepChild(name)
if (maybeDeepChild ~= nil) then
return maybeDeepChild
end
end
end
end
local function addChild(self, element)
if (getChild(element:getName()) ~= nil) then
return
end
objId = objId + 1
local zIndex = element:getZIndex()
table.insert(children, {element = element, zIndex = zIndex, objId = objId})
sorted = false
element:setParent(self, true)
for event, _ in pairs(element:getRegisteredEvents()) do
self:addEvent(event, element)
end
if (element.init~=nil) then
element:init()
end
if (element.load~=nil) then
element:load()
end
if (element.draw~=nil) then
element:draw()
end
return element
end
local function removeChild(self, element)
if (type(element)=="string") then
element = getChild(element:getName())
end
if (element==nil) then
return
end
for i, v in ipairs(children) do
if v.element == element then
table.remove(children, i)
return true
end
end
self:removeEvents(element)
sorted = false
end
local function removeChildren(self)
local parent = self:getParent()
children = {}
events = {}
sorted = false
objId = 0
evId = 0
focusedChild = nil
parent:removeEvents(self)
end
local function updateZIndex(self, element, newZ)
objId = objId + 1
evId = evId + 1
for _,v in pairs(children)do
if(v.element==element)then
v.zIndex = newZ
v.objId = objId
break
end
end
for _,v in pairs(events)do
for a,b in pairs(v)do
if(b.element==element)then
b.zIndex = newZ
b.evId = evId
end
end
end
sorted = false
self:updateDraw()
end
local function removeEvents(self, element)
local parent = self:getParent()
for a, b in pairs(events) do
for c, d in pairs(b) do
if(d.element == element)then
table.remove(events[a], c)
end
end
if(tableCount(events[a])<=0)then
if(parent~=nil)then
parent:removeEvent(a, self)
end
end
end
sorted = false
end
local function getEvent(self, event, name)
if(type(name)=="table")then name = name:getName() end
if(events[event]~=nil)then
for _, obj in pairs(events[event]) do
if (obj.element:getName() == name) then
return obj
end
end
end
end
local function addEvent(self, event, element)
if (getEvent(self, event, element:getName()) ~= nil) then
return
end
local zIndex = element:getZIndex()
evId = evId + 1
if(events[event]==nil)then events[event] = {} end
table.insert(events[event], {element = element, zIndex = zIndex, evId = evId})
sorted = false
self:listenEvent(event)
return element
end
local function removeEvent(self, event, element)
if(events[event]~=nil)then
for a, b in pairs(events[event]) do
if(b.element == element)then
table.remove(events[event], a)
end
end
if(tableCount(events[event])<=0)then
self:listenEvent(event, false)
end
end
sorted = false
end
local function getEvents(self, event)
return event~=nil and events[event] or events
end
container = {
getType = function()
return objectType
end,
getBase = function(self)
return base
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
setSize = function(self, ...)
base.setSize(self, ...)
self:customEventHandler("basalt_FrameResize")
return self
end,
setPosition = function(self, ...)
base.setPosition(self, ...)
self:customEventHandler("basalt_FrameReposition")
return self
end,
searchChildren = function(self, name)
local results = {}
for _, child in pairs(children) do
if (string.find(child.element:getName(), name)) then
table.insert(results, child)
end
end
return results
end,
getChildrenByType = function(self, type)
local results = {}
for _, child in pairs(children) do
if (child.element:isType(type)) then
table.insert(results, child)
end
end
return results
end,
setImportant = function(self, element)
objId = objId + 1
evId = evId + 1
for a, b in pairs(events) do
for c, d in pairs(b) do
if(d.element == element)then
d.evId = evId
table.remove(events[a], c)
table.insert(events[a], d)
break
end
end
end
for i, v in ipairs(children) do
if v.element == element then
v.objId = objId
table.remove(children, i)
table.insert(children, v)
break
end
end
if(self.updateDraw~=nil)then
self:updateDraw()
end
sorted = false
end,
sortChildren = function(self)
if (sorted) then
return
end
table.sort(children, objSort)
for event, _ in pairs(events) do
table.sort(events[event], evSort)
end
sorted = true
end,
clearFocusedChild = function(self)
if(focusedChild~=nil)then
if(getChild(self, focusedChild)~=nil)then
focusedChild:loseFocusHandler()
end
end
focusedChild = nil
return self
end,
setFocusedChild = function(self, obj)
if(focusedChild~=obj)then
if(focusedChild~=nil)then
if(getChild(self, focusedChild)~=nil)then
focusedChild:loseFocusHandler()
end
end
if(obj~=nil)then
if(getChild(self, obj)~=nil)then
obj:getFocusHandler()
end
end
focusedChild = obj
return true
end
return false
end,
getFocused = function(self)
return focusedChild
end,
getChild = getChild,
getChildren = getChildren,
getDeepChildren = getDeepChild,
addChild = addChild,
removeChild = removeChild,
removeChildren = removeChildren,
getEvents = getEvents,
getEvent = getEvent,
addEvent = addEvent,
removeEvent = removeEvent,
removeEvents = removeEvents,
updateZIndex = updateZIndex,
listenEvent = function(self, event, active)
base.listenEvent(self, event, active)
if(events[event]==nil)then events[event] = {} end
return self
end,
customEventHandler = function(self, ...)
base.customEventHandler(self, ...)
for _, o in pairs(children) do
if (o.element.customEventHandler ~= nil) then
o.element:customEventHandler(...)
end
end
end,
loseFocusHandler = function(self)
base.loseFocusHandler(self)
if(focusedChild~=nil)then focusedChild:loseFocusHandler() focusedChild = nil end
end,
getBasalt = function(self)
return basalt
end,
setPalette = function(self, col, ...)
local parent = self:getParent()
parent:setPalette(col, ...)
return self
end,
eventHandler = function(self, ...)
if(base.eventHandler~=nil)then
base.eventHandler(self, ...)
if(events["other_event"]~=nil)then
self:sortChildren()
for _, obj in ipairs(events["other_event"]) do
if (obj.element.eventHandler ~= nil) then
obj.element.eventHandler(obj.element, ...)
end
end
end
end
end,
}
for k,v in pairs({mouse_click={"mouseHandler", true},mouse_up={"mouseUpHandler", false},mouse_drag={"dragHandler", false},mouse_scroll={"scrollHandler", true},mouse_hover={"hoverHandler", false}})do
container[v[1]] = function(self, btn, x, y, ...)
if(base[v[1]]~=nil)then
if(base[v[1]](self, btn, x, y, ...))then
if(events[k]~=nil)then
self:sortChildren()
for _, obj in ipairs(events[k]) do
if (obj.element[v[1]] ~= nil) then
local xO, yO = 0, 0
if(self.getOffset~=nil)then
xO, yO = self:getOffset()
end
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
end
end
end
if(v[2])then
self:clearFocusedChild()
end
end
return true
end
end
end
end
for k,v in pairs({key="keyHandler",key_up="keyUpHandler",char="charHandler"})do
container[v] = function(self, ...)
if(base[v]~=nil)then
if(base[v](self, ...))then
if(events[k]~=nil)then
self:sortChildren()
for _, obj in ipairs(events[k]) do
if (obj.element[v] ~= nil) then
if (obj.element[v](obj.element, ...)) then
return true
end
end
end
end
end
end
end
end
for objectName, _ in pairs(basalt.getObjects()) do
container["add" .. objectName] = function(self, id)
return self:addChild(basalt:createObject(objectName, id))
end
end
container.__index = container
return setmetatable(container, base)
end

View File

@@ -0,0 +1,248 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("List")(name, basalt)
local objectType = "Dropdown"
base:setSize(12, 1)
base:setZIndex(6)
local selectionColorActive = true
local align = "left"
local yOffset = 0
local dropdownW = 0
local dropdownH = 0
local autoSize = true
local closedSymbol = "\16"
local openedSymbol = "\31"
local isOpened = false
local object = {
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
load = function(self)
self:listenEvent("mouse_click", self)
self:listenEvent("mouse_up", self)
self:listenEvent("mouse_scroll", self)
self:listenEvent("mouse_drag", self)
end,
setOffset = function(self, yOff)
yOffset = yOff
self:updateDraw()
return self
end,
getOffset = function(self)
return yOffset
end,
addItem = function(self, t, ...)
base.addItem(self, t, ...)
if(autoSize)then
dropdownW = math.max(dropdownW, #t)
dropdownH = dropdownH + 1
end
return self
end,
removeItem = function(self, index)
base.removeItem(self, index)
if(autoSize)then
dropdownW = 0
dropdownH = 0
for n = 1, #list do
dropdownW = math.max(dropdownW, #list[n].text)
end
dropdownH = #list
end
end,
isOpened = function(self)
return isOpened
end,
setOpened = function(self, open)
isOpened = open
self:updateDraw()
return self
end,
setDropdownSize = function(self, width, height)
dropdownW, dropdownH = width, height
autoSize = false
self:updateDraw()
return self
end,
setDropdownWidth = function(self, width)
return self:setDropdownSize(width, dropdownH)
end,
setDropdownHeight = function(self, height)
return self:setDropdownSize(dropdownW, height)
end,
getDropdownSize = function(self)
return dropdownW, dropdownH
end,
getDropdownWidth = function(self)
return dropdownW
end,
getDropdownHeight = function(self)
return dropdownH
end,
mouseHandler = function(self, button, x, y, isMon)
if (isOpened) then
local obx, oby = self:getAbsolutePosition()
if(button==1)then
local list = self:getAll()
if (#list > 0) then
for n = 1, dropdownH do
if (list[n + yOffset] ~= nil) then
if (obx <= x) and (obx + dropdownW > x) and (oby + n == y) then
self:setValue(list[n + yOffset])
self:updateDraw()
local val = self:sendEvent("mouse_click", self, "mouse_click", button, x, y)
if(val==false)then return val end
if(isMon)then
basalt.schedule(function()
sleep(0.1)
self:mouseUpHandler(button, x, y)
end)()
end
return true
end
end
end
end
end
end
local base = base:getBase()
if (base.mouseHandler(self, button, x, y)) then
isOpened = not isOpened
self:getParent():setImportant(self)
self:updateDraw()
return true
else
if(isOpened)then
self:updateDraw()
isOpened = false
end
return false
end
end,
mouseUpHandler = function(self, button, x, y)
if (isOpened) then
local obx, oby = self:getAbsolutePosition()
if(button==1)then
local list = self:getAll()
if (#list > 0) then
for n = 1, dropdownH do
if (list[n + yOffset] ~= nil) then
if (obx <= x) and (obx + dropdownW > x) and (oby + n == y) then
isOpened = false
self:updateDraw()
local val = self:sendEvent("mouse_up", self, "mouse_up", button, x, y)
if(val==false)then return val end
return true
end
end
end
end
end
end
end,
dragHandler = function(self, btn, x, y)
if(base.dragHandler(self, btn, x, y))then
isOpened = true
end
end,
scrollHandler = function(self, dir, x, y)
if(isOpened)then
local xPos, yPos = self:getAbsolutePosition()
if(x >= xPos)and(x <= xPos + dropdownW)and(y >= yPos)and(y <= yPos + dropdownH)then
self:setFocus()
end
end
if (isOpened)and(self:isFocused()) then
local xPos, yPos = self:getAbsolutePosition()
if(x < xPos)or(x > xPos + dropdownW)or(y < yPos)or(y > yPos + dropdownH)then
return false
end
if(#self:getAll() <= dropdownH)then return false end
local list = self:getAll()
yOffset = yOffset + dir
if (yOffset < 0) then
yOffset = 0
end
if (dir == 1) then
if (#list > dropdownH) then
if (yOffset > #list - dropdownH) then
yOffset = #list - dropdownH
end
else
yOffset = math.min(#list - 1, 0)
end
end
local val = self:sendEvent("mouse_scroll", self, "mouse_scroll", dir, x, y)
if(val==false)then return val end
self:updateDraw()
return true
end
end,
draw = function(self)
base.draw(self)
self:setDrawState("list", false)
self:addDraw("dropdown", function()
local obx, oby = self:getPosition()
local w,h = self:getSize()
local val = self:getValue()
local list = self:getAll()
local bgCol, fgCol = self:getBackground(), self:getForeground()
local text = utils.getTextHorizontalAlign((val~=nil and val.text or ""), w, align):sub(1, w - 1) .. (isOpened and openedSymbol or closedSymbol)
self:addBlit(1, 1, text, tHex[fgCol]:rep(#text), tHex[bgCol]:rep(#text))
if (isOpened) then
self:addTextBox(1, 2, dropdownW, dropdownH, " ")
self:addBackgroundBox(1, 2, dropdownW, dropdownH, bgCol)
self:addForegroundBox(1, 2, dropdownW, dropdownH, fgCol)
for n = 1, dropdownH do
if (list[n + yOffset] ~= nil) then
local t =utils.getTextHorizontalAlign(list[n + yOffset].text, dropdownW, align)
if (list[n + yOffset] == val) then
if (selectionColorActive) then
local itemSelectedBG, itemSelectedFG = self:getSelectionColor()
self:addBlit(1, n+1, t, tHex[itemSelectedFG]:rep(#t), tHex[itemSelectedBG]:rep(#t))
else
self:addBlit(1, n+1, t, tHex[list[n + yOffset].fgCol]:rep(#t), tHex[list[n + yOffset].bgCol]:rep(#t))
end
else
self:addBlit(1, n+1, t, tHex[list[n + yOffset].fgCol]:rep(#t), tHex[list[n + yOffset].bgCol]:rep(#t))
end
end
end
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,433 @@
local function flexObjectPlugin(base, basalt)
local flexGrow = 0
local flexShrink = 0
local flexBasis = 0
local baseWidth, baseHeight = base:getSize()
local object = {
getFlexGrow = function(self)
return flexGrow
end,
setFlexGrow = function(self, value)
flexGrow = value
return self
end,
getFlexShrink = function(self)
return flexShrink
end,
setFlexShrink = function(self, value)
flexShrink = value
return self
end,
getFlexBasis = function(self)
return flexBasis
end,
setFlexBasis = function(self, value)
flexBasis = value
return self
end,
getSize = function(self)
return baseWidth, baseHeight
end,
getWidth = function(self)
return baseWidth
end,
getHeight = function(self)
return baseHeight
end,
setSize = function(self, width, height, rel, internalCall)
base.setSize(self, width, height, rel)
if not internalCall then
baseWidth, baseHeight = base:getSize()
end
return self
end,
}
object.__index = object
return setmetatable(object, base)
end
return function(name, basalt)
local base = basalt.getObject("ScrollableFrame")(name, basalt)
local objectType = "Flexbox"
local direction = "row"
local spacing = 1
local justifyContent = "flex-start"
local wrap = "nowrap"
local children = {}
local sortedChildren = {}
local updateLayout = false
local lineBreakFakeObject = flexObjectPlugin({
getHeight = function(self) return 0 end,
getWidth = function(self) return 0 end,
getPosition = function(self) return 0, 0 end,
getSize = function(self) return 0, 0 end,
isType = function(self) return false end,
getType = function(self) return "lineBreakFakeObject" end,
setPosition = function(self) end,
setSize = function(self) end,
})
lineBreakFakeObject:setFlexBasis(0):setFlexGrow(0):setFlexShrink(0)
local function sortChildren(self)
if(wrap=="nowrap")then
sortedChildren = {}
local index = 1
local lineSize = 1
local lineOffset = 1
for _,v in pairs(children)do
if(sortedChildren[index]==nil)then sortedChildren[index]={offset=1} end
local childHeight = direction == "row" and v:getHeight() or v:getWidth()
if childHeight > lineSize then
lineSize = childHeight
end
if(v == lineBreakFakeObject)then
lineOffset = lineOffset + lineSize + spacing
lineSize = 1
index = index + 1
sortedChildren[index] = {offset=lineOffset}
else
table.insert(sortedChildren[index], v)
end
end
elseif(wrap=="wrap")then
sortedChildren = {}
local lineSize = 1
local lineOffset = 1
local maxSize = direction == "row" and self:getWidth() or self:getHeight()
local usedSize = 0
local index = 1
for _,v in pairs(children) do
if(sortedChildren[index]==nil) then sortedChildren[index]={offset=1} end
if v == lineBreakFakeObject then
lineOffset = lineOffset + lineSize + spacing
usedSize = 0
lineSize = 1
index = index + 1
sortedChildren[index] = {offset=lineOffset}
else
local objSize = direction == "row" and v:getWidth() or v:getHeight()
if(objSize+usedSize<=maxSize) then
table.insert(sortedChildren[index], v)
usedSize = usedSize + objSize + spacing
else
lineOffset = lineOffset + lineSize + spacing
lineSize = direction == "row" and v:getHeight() or v:getWidth()
index = index + 1
usedSize = objSize + spacing
sortedChildren[index] = {offset=lineOffset, v}
end
local childHeight = direction == "row" and v:getHeight() or v:getWidth()
if childHeight > lineSize then
lineSize = childHeight
end
end
end
end
end
local function calculateRow(self, children)
local containerWidth, containerHeight = self:getSize()
local totalFlexGrow = 0
local totalFlexShrink = 0
local totalFlexBasis = 0
for _, child in ipairs(children) do
totalFlexGrow = totalFlexGrow + child:getFlexGrow()
totalFlexShrink = totalFlexShrink + child:getFlexShrink()
totalFlexBasis = totalFlexBasis + child:getFlexBasis()
end
local remainingSpace = containerWidth - totalFlexBasis - (spacing * (#children - 1))
local currentX = 1
for _, child in ipairs(children) do
if(child~=lineBreakFakeObject)then
local childWidth
local flexGrow = child:getFlexGrow()
local flexShrink = child:getFlexShrink()
local baseWidth = child:getFlexBasis() ~= 0 and child:getFlexBasis() or child:getWidth()
if totalFlexGrow > 0 then
childWidth = baseWidth + flexGrow / totalFlexGrow * remainingSpace
else
childWidth = baseWidth
end
if remainingSpace < 0 and totalFlexShrink > 0 then
childWidth = baseWidth + flexShrink / totalFlexShrink * remainingSpace
end
child:setPosition(currentX, children.offset or 1)
child:setSize(childWidth, child:getHeight(), false, true)
currentX = currentX + childWidth + spacing
end
end
if justifyContent == "flex-end" then
local totalWidth = currentX - spacing
local offset = containerWidth - totalWidth + 1
for _, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x + offset, y)
end
elseif justifyContent == "center" then
local totalWidth = currentX - spacing
local offset = (containerWidth - totalWidth) / 2 + 1
for _, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x + offset, y)
end
elseif justifyContent == "space-between" then
local totalWidth = currentX - spacing
local offset = (containerWidth - totalWidth) / (#children - 1) + 1
for i, child in ipairs(children) do
if i > 1 then
local x, y = child:getPosition()
child:setPosition(x + offset * (i - 1), y)
end
end
elseif justifyContent == "space-around" then
local totalWidth = currentX - spacing
local offset = (containerWidth - totalWidth) / #children
for i, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x + offset * i - offset / 2, y)
end
elseif justifyContent == "space-evenly" then
local numSpaces = #children + 1
local totalChildWidth = 0
for _, child in ipairs(children) do
totalChildWidth = totalChildWidth + child:getWidth()
end
local totalSpace = containerWidth - totalChildWidth
local offset = math.floor(totalSpace / numSpaces)
local remaining = totalSpace - offset * numSpaces
currentX = offset + (remaining > 0 and 1 or 0)
remaining = remaining > 0 and remaining - 1 or 0
for _, child in ipairs(children) do
child:setPosition(currentX, 1)
currentX = currentX + child:getWidth() + offset + (remaining > 0 and 1 or 0)
remaining = remaining > 0 and remaining - 1 or 0
end
end
end
local function calculateColumn(self, children)
local containerWidth, containerHeight = self:getSize()
local totalFlexGrow = 0
local totalFlexShrink = 0
local totalFlexBasis = 0
for _, child in ipairs(children) do
totalFlexGrow = totalFlexGrow + child:getFlexGrow()
totalFlexShrink = totalFlexShrink + child:getFlexShrink()
totalFlexBasis = totalFlexBasis + child:getFlexBasis()
end
local remainingSpace = containerHeight - totalFlexBasis - (spacing * (#children - 1))
local currentY = 1
for _, child in ipairs(children) do
if(child~=lineBreakFakeObject)then
local childHeight
local flexGrow = child:getFlexGrow()
local flexShrink = child:getFlexShrink()
local baseHeight = child:getFlexBasis() ~= 0 and child:getFlexBasis() or child:getHeight()
if totalFlexGrow > 0 then
childHeight = baseHeight + flexGrow / totalFlexGrow * remainingSpace
else
childHeight = baseHeight
end
if remainingSpace < 0 and totalFlexShrink > 0 then
childHeight = baseHeight + flexShrink / totalFlexShrink * remainingSpace
end
child:setPosition(children.offset, currentY)
child:setSize(child:getWidth(), childHeight, false, true)
currentY = currentY + childHeight + spacing
end
end
if justifyContent == "flex-end" then
local totalHeight = currentY - spacing
local offset = containerHeight - totalHeight + 1
for _, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x, y + offset)
end
elseif justifyContent == "center" then
local totalHeight = currentY - spacing
local offset = (containerHeight - totalHeight) / 2
for _, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x, y + offset)
end
elseif justifyContent == "space-between" then
local totalHeight = currentY - spacing
local offset = (containerHeight - totalHeight) / (#children - 1) + 1
for i, child in ipairs(children) do
if i > 1 then
local x, y = child:getPosition()
child:setPosition(x, y + offset * (i - 1))
end
end
elseif justifyContent == "space-around" then
local totalHeight = currentY - spacing
local offset = (containerHeight - totalHeight) / #children
for i, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x, y + offset * i - offset / 2)
end
elseif justifyContent == "space-evenly" then
local numSpaces = #children + 1
local totalChildHeight = 0
for _, child in ipairs(children) do
totalChildHeight = totalChildHeight + child:getHeight()
end
local totalSpace = containerHeight - totalChildHeight
local offset = math.floor(totalSpace / numSpaces)
local remaining = totalSpace - offset * numSpaces
currentY = offset + (remaining > 0 and 1 or 0)
remaining = remaining > 0 and remaining - 1 or 0
for _, child in ipairs(children) do
local x, y = child:getPosition()
child:setPosition(x, currentY)
currentY = currentY + child:getHeight() + offset + (remaining > 0 and 1 or 0)
remaining = remaining > 0 and remaining - 1 or 0
end
end
end
local function applyLayout(self)
sortChildren(self)
if direction == "row" then
for _,v in pairs(sortedChildren)do
calculateRow(self, v)
end
else
for _,v in pairs(sortedChildren)do
calculateColumn(self, v)
end
end
updateLayout = false
end
local object = {
getType = function()
return objectType
end,
isType = function(self, t)
return objectType == t or base.isType ~= nil and base.isType(t) or false
end,
setJustifyContent = function(self, value)
justifyContent = value
updateLayout = true
self:updateDraw()
return self
end,
getJustifyContent = function(self)
return justifyContent
end,
setDirection = function(self, value)
direction = value
updateLayout = true
self:updateDraw()
return self
end,
getDirection = function(self)
return direction
end,
setSpacing = function(self, value)
spacing = value
updateLayout = true
self:updateDraw()
return self
end,
getSpacing = function(self)
return spacing
end,
setWrap = function(self, value)
wrap = value
updateLayout = true
self:updateDraw()
return self
end,
getWrap = function(self)
return wrap
end,
updateLayout = function(self)
updateLayout = true
self:updateDraw()
end,
addBreak = function(self)
table.insert(children, lineBreakFakeObject)
updateLayout = true
self:updateDraw()
return self
end,
customEventHandler = function(self, event, ...)
base.customEventHandler(self, event, ...)
if event == "basalt_FrameResize" then
updateLayout = true
end
end,
draw = function(self)
base.draw(self)
self:addDraw("flexboxDraw", function()
if updateLayout then
applyLayout(self)
end
end, 1)
end
}
for k, _ in pairs(basalt.getObjects()) do
object["add" .. k] = function(self, name)
local baseChild = base["add" .. k](self, name)
local child = flexObjectPlugin(baseChild, basalt)
table.insert(children, child)
updateLayout = true
return child
end
end
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,133 @@
local utils = require("utils")
local max,min,sub,rep,len = math.max,math.min,string.sub,string.rep,string.len
return function(name, basalt)
local base = basalt.getObject("Container")(name, basalt)
local objectType = "Frame"
local parent
local updateRender = true
local xOffset, yOffset = 0, 0
base:setSize(30, 10)
base:setZIndex(10)
local object = {
getType = function()
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
getBase = function(self)
return base
end,
getOffset = function(self)
return xOffset, yOffset
end,
setOffset = function(self, xOff, yOff)
xOffset = xOff or xOffset
yOffset = yOff or yOffset
self:updateDraw()
return self
end,
getXOffset = function(self)
return xOffset
end,
setXOffset = function(self, newXOffset)
return self:setOffset(newXOffset, nil)
end,
getYOffset = function(self)
return yOffset
end,
setYOffset = function(self, newYOffset)
return self:setOffset(nil, newYOffset)
end,
setParent = function(self, p, ...)
base.setParent(self, p, ...)
parent = p
return self
end,
render = function(self)
if(base.render~=nil)then
if(self:isVisible())then
base.render(self)
local children = self:getChildren()
for _, child in ipairs(children) do
if (child.element.render ~= nil) then
child.element:render()
end
end
end
end
end,
updateDraw = function(self)
if(parent~=nil)then
parent:updateDraw()
end
return self
end,
blit = function (self, x, y, t, f, b)
local obx, oby = self:getPosition()
local xO, yO = parent:getOffset()
obx = obx - xO
oby = oby - yO
local w, h = self:getSize()
if y >= 1 and y <= h then
local t_visible = sub(t, max(1 - x + 1, 1), max(w - x + 1, 1))
local f_visible = sub(f, max(1 - x + 1, 1), max(w - x + 1, 1))
local b_visible = sub(b, max(1 - x + 1, 1), max(w - x + 1, 1))
parent:blit(max(x + (obx - 1), obx), oby + y - 1, t_visible, f_visible, b_visible)
end
end,
setCursor = function(self, blink, x, y, color)
local obx, oby = self:getPosition()
local xO, yO = self:getOffset()
parent:setCursor(blink or false, (x or 0)+obx-1 - xO, (y or 0)+oby-1 - yO, color or colors.white)
return self
end,
}
for k,v in pairs({"drawBackgroundBox", "drawForegroundBox", "drawTextBox"})do
object[v] = function(self, x, y, width, height, symbol)
local obx, oby = self:getPosition()
local xO, yO = parent:getOffset()
obx = obx - xO
oby = oby - yO
height = (y < 1 and (height + y > self:getHeight() and self:getHeight() or height + y - 1) or (height + y > self:getHeight() and self:getHeight() - y + 1 or height))
width = (x < 1 and (width + x > self:getWidth() and self:getWidth() or width + x - 1) or (width + x > self:getWidth() and self:getWidth() - x + 1 or width))
parent[v](parent, max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, symbol)
end
end
for k,v in pairs({"setBG", "setFG", "setText"})do
object[v] = function(self, x, y, str)
local obx, oby = self:getPosition()
local xO, yO = parent:getOffset()
obx = obx - xO
oby = oby - yO
local w, h = self:getSize()
if (y >= 1) and (y <= h) then
parent[v](parent, max(x + (obx - 1), obx), oby + y - 1, sub(str, max(1 - x + 1, 1), max(w - x + 1,1)))
end
end
end
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,171 @@
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Graph"
base:setZIndex(5)
base:setSize(30, 10)
local graphData = {}
local graphColor = colors.gray
local graphSymbol = "\7"
local graphSymbolCol = colors.black
local maxValue = 100
local minValue = 0
local graphType = "line"
local maxEntries = 10
local object = {
getType = function(self)
return objectType
end,
setGraphColor = function(self, color)
graphColor = color or graphColor
self:updateDraw()
return self
end,
setGraphSymbol = function(self, symbol, symbolcolor)
graphSymbol = symbol or graphSymbol
graphSymbolCol = symbolcolor or graphSymbolCol
self:updateDraw()
return self
end,
setGraphSymbolColor = function(self, symbolColor)
return self:setGraphSymbolColor(nil, symbolColor)
end,
getGraphSymbol = function(self)
return graphSymbol, graphSymbolCol
end,
getGraphSymbolColor = function(self)
return graphSymbolCol
end,
addDataPoint = function(self, value)
if value >= minValue and value <= maxValue then
table.insert(graphData, value)
self:updateDraw()
end
if(#graphData>100)then -- 100 is hard capped to prevent memory leaks
table.remove(graphData,1)
end
return self
end,
setMaxValue = function(self, value)
maxValue = value
self:updateDraw()
return self
end,
getMaxValue = function(self)
return maxValue
end,
setMinValue = function(self, value)
minValue = value
self:updateDraw()
return self
end,
getMinValue = function(self)
return minValue
end,
setGraphType = function(self, graph_type)
if graph_type == "scatter" or graph_type == "line" or graph_type == "bar" then
graphType = graph_type
self:updateDraw()
end
return self
end,
getGraphType = function(self)
return graphType
end,
setMaxEntries = function(self, value)
maxEntries = value
self:updateDraw()
return self
end,
getMaxEntries = function(self)
return maxEntries
end,
clear = function(self)
graphData = {}
self:updateDraw()
return self
end,
draw = function(self)
base.draw(self)
self:addDraw("graph", function()
local obx, oby = self:getPosition()
local w, h = self:getSize()
local bgCol, fgCol = self:getBackground(), self:getForeground()
local range = maxValue - minValue
local prev_x, prev_y
local startIndex = #graphData - maxEntries + 1
if startIndex < 1 then startIndex = 1 end
for i = startIndex, #graphData do
local data = graphData[i]
local x = math.floor(((w - 1) / (maxEntries - 1)) * (i - startIndex) + 1.5)
local y = math.floor((h - 1) - ((h - 1) / range) * (data - minValue) + 1.5)
if graphType == "scatter" then
self:addBackgroundBox(x, y, 1, 1, graphColor)
self:addForegroundBox(x, y, 1, 1, graphSymbolCol)
self:addTextBox(x, y, 1, 1, graphSymbol)
elseif graphType == "line" then
if prev_x and prev_y then
local dx = math.abs(x - prev_x)
local dy = math.abs(y - prev_y)
local sx = prev_x < x and 1 or -1
local sy = prev_y < y and 1 or -1
local err = dx - dy
while true do
self:addBackgroundBox(prev_x, prev_y, 1, 1, graphColor)
self:addForegroundBox(prev_x, prev_y, 1, 1, graphSymbolCol)
self:addTextBox(prev_x, prev_y, 1, 1, graphSymbol)
if prev_x == x and prev_y == y then
break
end
local e2 = 2 * err
if e2 > -dy then
err = err - dy
prev_x = prev_x + sx
end
if e2 < dx then
err = err + dx
prev_y = prev_y + sy
end
end
end
prev_x, prev_y = x, y
elseif graphType == "bar" then
self:addBackgroundBox(x - 1, y, 1, h - y, graphColor)
end
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,363 @@
local images = require("images")
local bimg = require("bimg")
local unpack,sub,max,min = table.unpack,string.sub,math.max,math.min
return function(name, basalt)
-- Image
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Image"
local bimgLibrary = bimg()
local bimgFrame = bimgLibrary.getFrameObject(1)
local originalImage
local image
local activeFrame = 1
local infinitePlay = false
local animTimer
local usePalette = false
local autoSize = true
local xOffset, yOffset = 0, 0
base:setSize(24, 8)
base:setZIndex(2)
local function getPalette(id)
local p = {}
for k,v in pairs(colors)do
if(type(v)=="number")then
p[k] = {term.nativePaletteColor(v)}
end
end
local globalPalette = bimgLibrary.getMetadata("palette")
if(globalPalette~=nil)then
for k,v in pairs(globalPalette)do
p[k] = tonumber(v)
end
end
local localPalette = bimgLibrary.getFrameData("palette")
basalt.log(localPalette)
if(localPalette~=nil)then
for k,v in pairs(localPalette)do
p[k] = tonumber(v)
end
end
return p
end
local function checkAutoSize()
if(autoSize)then
if(bimgLibrary~=nil)then
base:setSize(bimgLibrary.getSize())
end
end
end
local object = {
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
setOffset = function(self, _x, _y, rel)
if(rel)then
xOffset = xOffset + _x or 0
yOffset = yOffset + _y or 0
else
xOffset = _x or xOffset
yOffset = _y or yOffset
end
self:updateDraw()
return self
end,
setXOffset = function(self, _x)
return self:setOffset(self, _x, nil)
end,
setYOffset = function(self, _y)
return self:setOffset(self, nil, _y)
end,
setSize = function(self, _x, _y)
base:setSize(_x, _y)
autoSize = false
return self
end,
getOffset = function(self)
return xOffset, yOffset
end,
getXOffset = function(self)
return xOffset
end,
getYOffset = function(self)
return yOffset
end,
selectFrame = function(self, id)
if(bimgLibrary.getFrameObject(id)==nil)then
bimgLibrary.addFrame(id)
end
bimgFrame = bimgLibrary.getFrameObject(id)
image = bimgFrame.getImage(id)
activeFrame = id
self:updateDraw()
end,
addFrame = function(self, id)
bimgLibrary.addFrame(id)
return self
end,
getFrame = function(self, id)
return bimgLibrary.getFrame(id)
end,
getFrameObject = function(self, id)
return bimgLibrary.getFrameObject(id)
end,
removeFrame = function(self, id)
bimgLibrary.removeFrame(id)
return self
end,
moveFrame = function(self, id, dir)
bimgLibrary.moveFrame(id, dir)
return self
end,
getFrames = function(self)
return bimgLibrary.getFrames()
end,
getFrameCount = function(self)
return #bimgLibrary.getFrames()
end,
getActiveFrame = function(self)
return activeFrame
end,
loadImage = function(self, path)
if(fs.exists(path))then
local newBimg = images.loadBIMG(path)
bimgLibrary = bimg(newBimg)
activeFrame = 1
bimgFrame = bimgLibrary.getFrameObject(1)
originalImage = bimgLibrary.createBimg()
image = bimgFrame.getImage()
checkAutoSize()
self:updateDraw()
end
return self
end,
setPath = function(self, path)
return self:loadImage(path)
end,
setImage = function(self, t)
if(type(t)=="table")then
bimgLibrary = bimg(t)
activeFrame = 1
bimgFrame = bimgLibrary.getFrameObject(1)
originalImage = bimgLibrary.createBimg()
image = bimgFrame.getImage()
checkAutoSize()
self:updateDraw()
end
return self
end,
clear = function(self)
bimgLibrary = bimg()
bimgFrame = bimgLibrary.getFrameObject(1)
image = nil
self:updateDraw()
return self
end,
getImage = function(self)
return bimgLibrary.createBimg()
end,
getImageFrame = function(self, id)
return bimgFrame.getImage(id)
end,
usePalette = function(self, use)
usePalette = use~=nil and use or true
return self
end,
getUsePalette = function(self)
return usePalette
end,
setUsePalette = function(self, use)
return self:usePalette(use)
end,
play = function(self, inf)
if(bimgLibrary.getMetadata("animated"))then
local t = bimgLibrary.getMetadata("duration") or bimgLibrary.getMetadata("secondsPerFrame") or 0.2
self:listenEvent("other_event")
animTimer = os.startTimer(t)
infinitePlay = inf or false
end
return self
end,
setPlay = function(self, inf)
return self:play(inf)
end,
stop = function(self)
os.cancelTimer(animTimer)
animTimer = nil
infinitePlay = false
return self
end,
eventHandler = function(self, event, timerId, ...)
base.eventHandler(self, event, timerId, ...)
if(event=="timer")then
if(timerId==animTimer)then
if(bimgLibrary.getFrame(activeFrame+1)~=nil)then
activeFrame = activeFrame + 1
self:selectFrame(activeFrame)
local t = bimgLibrary.getFrameData(activeFrame, "duration") or bimgLibrary.getMetadata("secondsPerFrame") or 0.2
animTimer = os.startTimer(t)
else
if(infinitePlay)then
activeFrame = 1
self:selectFrame(activeFrame)
local t = bimgLibrary.getFrameData(activeFrame, "duration") or bimgLibrary.getMetadata("secondsPerFrame") or 0.2
animTimer = os.startTimer(t)
end
end
self:updateDraw()
end
end
end,
setMetadata = function(self, key, value)
bimgLibrary.setMetadata(key, value)
return self
end,
getMetadata = function(self, key)
return bimgLibrary.getMetadata(key)
end,
getFrameMetadata = function(self, id, key)
return bimgLibrary.getFrameData(id, key)
end,
setFrameMetadata = function(self, id, key, value)
bimgLibrary.setFrameData(id, key, value)
return self
end,
blit = function(self, text, fg, bg, _x, _y)
x = _x or x
y = _y or y
bimgFrame.blit(text, fg, bg, x, y)
image = bimgFrame.getImage()
self:updateDraw()
return self
end,
setText = function(self, text, _x, _y)
x = _x or x
y = _y or y
bimgFrame.text(text, x, y)
image = bimgFrame.getImage()
self:updateDraw()
return self
end,
setBg = function(self, bg, _x, _y)
x = _x or x
y = _y or y
bimgFrame.bg(bg, x, y)
image = bimgFrame.getImage()
self:updateDraw()
return self
end,
setFg = function(self, fg, _x, _y)
x = _x or x
y = _y or y
bimgFrame.fg(fg, x, y)
image = bimgFrame.getImage()
self:updateDraw()
return self
end,
getImageSize = function(self)
return bimgLibrary.getSize()
end,
setImageSize = function(self, w, h)
bimgLibrary.setSize(w, h)
image = bimgFrame.getImage()
self:updateDraw()
return self
end,
resizeImage = function(self, w, h)
local newBimg = images.resizeBIMG(originalImage, w, h)
bimgLibrary = bimg(newBimg)
activeFrame = 1
bimgFrame = bimgLibrary.getFrameObject(1)
image = bimgFrame.getImage()
self:updateDraw()
return self
end,
draw = function(self)
base.draw(self)
self:addDraw("image", function()
local w,h = self:getSize()
local x, y = self:getPosition()
local wParent, hParent = self:getParent():getSize()
local parentXOffset, parentYOffset = self:getParent():getOffset()
if(x - parentXOffset > wParent)or(y - parentYOffset > hParent)or(x - parentXOffset + w < 1)or(y - parentYOffset + h < 1)then
return
end
if(usePalette)then
self:getParent():setPalette(getPalette(activeFrame))
end
if(image~=nil)then
for k,v in pairs(image)do
if(k+yOffset<=h)and(k+yOffset>=1)then
local t,f,b = v[1],v[2],v[3]
local startIdx = max(1 - xOffset, 1)
local endIdx = min(w - xOffset, #t)
t = sub(t, startIdx, endIdx)
f = sub(f, startIdx, endIdx)
b = sub(b, startIdx, endIdx)
self:addBlit(max(1 + xOffset, 1), k + yOffset, t, f, b)
end
end
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,312 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
-- Input
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Input"
local inputType = "text"
local inputLimit = 0
base:setZIndex(5)
base:setValue("")
base:setSize(12, 1)
local textX = 1
local wIndex = 1
local defaultText = ""
local defaultBGCol = colors.black
local defaultFGCol = colors.lightGray
local showingText = defaultText
local internalValueChange = false
local object = {
load = function(self)
self:listenEvent("mouse_click")
self:listenEvent("key")
self:listenEvent("char")
self:listenEvent("other_event")
self:listenEvent("mouse_drag")
end,
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
setDefaultFG = function(self, fCol)
return self:setDefaultText(self, defaultText, fCol, nil)
end,
setDefaultBG = function(self, bCol)
return self:setDefaultText(self, defaultText, nil, bCol)
end,
setDefaultText = function(self, text, fCol, bCol)
defaultText = text
defaultFGCol = fCol or defaultFGCol
defaultBGCol = bCol or defaultBGCol
if (self:isFocused()) then
showingText = ""
else
showingText = defaultText
end
self:updateDraw()
return self
end,
getDefaultText = function(self)
return defaultText, defaultFGCol, defaultBGCol
end,
setOffset = function(self, x)
wIndex = x
self:updateDraw()
return self
end,
getOffset = function(self)
return wIndex
end,
setTextOffset = function(self, x)
textX = x
self:updateDraw()
return self
end,
getTextOffset = function(self)
return textX
end,
setInputType = function(self, t)
inputType = t
self:updateDraw()
return self
end,
getInputType = function(self)
return inputType
end,
setValue = function(self, val)
base.setValue(self, tostring(val))
if not (internalValueChange) then
textX = tostring(val):len() + 1
wIndex = math.max(1, textX-self:getWidth()+1)
if(self:isFocused())then
local parent = self:getParent()
local obx, oby = self:getPosition()
parent:setCursor(true, obx + textX - wIndex, oby+math.floor(self:getHeight()/2), self:getForeground())
end
end
self:updateDraw()
return self
end,
getValue = function(self)
local val = base.getValue(self)
return inputType == "number" and tonumber(val) or val
end,
setInputLimit = function(self, limit)
inputLimit = tonumber(limit) or inputLimit
self:updateDraw()
return self
end,
getInputLimit = function(self)
return inputLimit
end,
getFocusHandler = function(self)
base.getFocusHandler(self)
local parent = self:getParent()
if (parent ~= nil) then
local obx, oby = self:getPosition()
showingText = ""
if(defaultText~="")then
self:updateDraw()
end
parent:setCursor(true, obx + textX - wIndex, oby+math.max(math.ceil(self:getHeight()/2-1, 1)), self:getForeground())
end
end,
loseFocusHandler = function(self)
base.loseFocusHandler(self)
local parent = self:getParent()
showingText = defaultText
if(defaultText~="")then
self:updateDraw()
end
parent:setCursor(false)
end,
keyHandler = function(self, key)
if (base.keyHandler(self, key)) then
local w,h = self:getSize()
local parent = self:getParent()
internalValueChange = true
if (key == keys.backspace) then
-- on backspace
local text = tostring(base.getValue())
if (textX > 1) then
self:setValue(text:sub(1, textX - 2) .. text:sub(textX, text:len()))
textX = math.max(textX - 1, 1)
if (textX < wIndex) then
wIndex = math.max(wIndex - 1, 1)
end
end
end
if (key == keys.enter) then
parent:clearFocusedChild(self)
end
if (key == keys.right) then
local tLength = tostring(base.getValue()):len()
textX = textX + 1
if (textX > tLength) then
textX = tLength + 1
end
textX = math.max(textX, 1)
if (textX < wIndex) or (textX >= w + wIndex) then
wIndex = textX - w + 1
end
wIndex = math.max(wIndex, 1)
end
if (key == keys.left) then
-- left arrow
textX = textX - 1
if (textX >= 1) then
if (textX < wIndex) or (textX >= w + wIndex) then
wIndex = textX
end
end
textX = math.max(textX, 1)
wIndex = math.max(wIndex, 1)
end
local obx, oby = self:getPosition()
local val = tostring(base.getValue())
self:updateDraw()
internalValueChange = false
return true
end
end,
charHandler = function(self, char)
if (base.charHandler(self, char)) then
internalValueChange = true
local w,h = self:getSize()
local text = base.getValue()
if (text:len() < inputLimit or inputLimit <= 0) then
if (inputType == "number") then
local cache = text
if (textX==1 and char == "-") or (char == ".") or (tonumber(char) ~= nil) then
self:setValue(text:sub(1, textX - 1) .. char .. text:sub(textX, text:len()))
textX = textX + 1
if(char==".")or(char=="-")and(#text>0)then
if (tonumber(base.getValue()) == nil) then
self:setValue(cache)
textX = textX - 1
end
end
end
else
self:setValue(text:sub(1, textX - 1) .. char .. text:sub(textX, text:len()))
textX = textX + 1
end
if (textX >= w + wIndex) then
wIndex = wIndex + 1
end
end
local obx, oby = self:getPosition()
local val = tostring(base.getValue())
internalValueChange = false
self:updateDraw()
return true
end
end,
mouseHandler = function(self, button, x, y)
if(base.mouseHandler(self, button, x, y))then
local parent = self:getParent()
local ax, ay = self:getPosition()
local obx, oby = self:getAbsolutePosition(ax, ay)
local w, h = self:getSize()
textX = x - obx + wIndex
local text = base.getValue()
if (textX > text:len()) then
textX = text:len() + 1
end
if (textX < wIndex) then
wIndex = textX - 1
if (wIndex < 1) then
wIndex = 1
end
end
parent:setCursor(true, ax + textX - wIndex, ay+math.max(math.ceil(h/2-1, 1)), self:getForeground())
return true
end
end,
dragHandler = function(self, btn, x, y, xOffset, yOffset)
if(self:isFocused())then
if(self:isCoordsInObject(x, y))then
if(base.dragHandler(self, btn, x, y, xOffset, yOffset))then
return true
end
end
local parent = self:getParent()
parent:clearFocusedChild()
end
end,
draw = function(self)
base.draw(self)
self:addDraw("input", function()
local parent = self:getParent()
local obx, oby = self:getPosition()
local w,h = self:getSize()
local verticalAlign = utils.getTextVerticalAlign(h, textVerticalAlign)
local val = tostring(base.getValue())
local bCol = self:getBackground()
local fCol = self:getForeground()
local text
if (val:len() <= 0) then
text = showingText
bCol = defaultBGCol or bCol
fCol = defaultFGCol or fCol
end
text = showingText
if (val ~= "") then
text = val
end
text = text:sub(wIndex, w + wIndex - 1)
local space = w - text:len()
if (space < 0) then
space = 0
end
if (inputType == "password") and (val ~= "") then
text = string.rep("*", text:len())
end
text = text .. string.rep(" ", space)
self:addBlit(1, verticalAlign, text, tHex[fCol]:rep(text:len()), tHex[bCol]:rep(text:len()))
if(self:isFocused())then
parent:setCursor(true, obx + textX - wIndex, oby+math.floor(self:getHeight()/2), self:getForeground())
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,115 @@
local utils = require("utils")
local wrapText = utils.wrapText
local writeWrappedText = utils.writeWrappedText
local tHex = require("tHex")
return function(name, basalt)
-- Label
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Label"
base:setZIndex(3)
base:setSize(5, 1)
base:setBackground(false)
local autoSize = true
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,
--- 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
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,
--- 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,
--- Gets the text alignment of the label.
--- @return string
getTextAlign = function(self)
return textAlign
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 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+1, h)
end)
end,
--- Initializes the label.
init = function(self)
base.init(self)
local parent = self:getParent()
self:setForeground(parent:getForeground())
end
}
object.__index = object
return setmetatable(object, base)
end

279
lib/basalt/objects/List.lua Normal file
View File

@@ -0,0 +1,279 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "List"
local list = {}
local itemSelectedBG = colors.black
local itemSelectedFG = colors.lightGray
local selectionColorActive = true
local textAlign = "left"
local yOffset = 0
local scrollable = true
base:setSize(16, 8)
base:setZIndex(5)
local object = {
init = function(self)
local parent = self:getParent()
self:listenEvent("mouse_click")
self:listenEvent("mouse_drag")
self:listenEvent("mouse_scroll")
return base.init(self)
end,
getBase = function(self)
return base
end,
setTextAlign = function(self, align)
textAlign = align
return self
end,
getTextAlign = function(self)
return textAlign
end,
getBase = function(self)
return base
end,
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
addItem = function(self, text, bgCol, fgCol, ...)
table.insert(list, { text = text, bgCol = bgCol or self:getBackground(), fgCol = fgCol or self:getForeground(), args = { ... } })
if (#list <= 1) then
self:setValue(list[1], false)
end
self:updateDraw()
return self
end,
setOptions = function(self, ...)
list = {}
for k,v in pairs(...)do
if(type(v)=="string")then
table.insert(list, { text = v, bgCol = self:getBackground(), fgCol = self:getForeground(), args = {} })
else
table.insert(list, { text = v[1], bgCol = v[2] or self:getBackground(), fgCol = v[3] or self:getForeground(), args = v[4] or {} })
end
end
self:setValue(list[1], false)
self:updateDraw()
return self
end,
setOffset = function(self, yOff)
yOffset = yOff
self:updateDraw()
return self
end,
getOffset = function(self)
return yOffset
end,
removeItem = function(self, index)
if(type(index)=="number")then
table.remove(list, index)
elseif(type(index)=="table")then
for k,v in pairs(list)do
if(v==index)then
table.remove(list, k)
break
end
end
end
self:updateDraw()
return self
end,
getItem = function(self, index)
return list[index]
end,
getAll = function(self)
return list
end,
getOptions = function(self)
return list
end,
getItemIndex = function(self)
local selected = self:getValue()
for key, value in pairs(list) do
if (value == selected) then
return key
end
end
end,
clear = function(self)
list = {}
self:setValue({}, false)
self:updateDraw()
return self
end,
getItemCount = function(self)
return #list
end,
editItem = function(self, index, text, bgCol, fgCol, ...)
table.remove(list, index)
table.insert(list, index, { text = text, bgCol = bgCol or self:getBackground(), fgCol = fgCol or self:getForeground(), args = { ... } })
self:updateDraw()
return self
end,
selectItem = function(self, index)
self:setValue(list[index] or {}, false)
self:updateDraw()
return self
end,
setSelectionColor = function(self, bgCol, fgCol, active)
itemSelectedBG = bgCol or self:getBackground()
itemSelectedFG = fgCol or self:getForeground()
selectionColorActive = active~=nil and active or true
self:updateDraw()
return self
end,
setSelectionBG = function(self, bgCol)
return self:setSelectionColor(bgCol, nil, selectionColorActive)
end,
setSelectionFG = function(self, fgCol)
return self:setSelectionColor(nil, fgCol, selectionColorActive)
end,
getSelectionColor = function(self)
return itemSelectedBG, itemSelectedFG
end,
getSelectionBG = function(self)
return itemSelectedBG
end,
getSelectionFG = function(self)
return itemSelectedFG
end,
isSelectionColorActive = function(self)
return selectionColorActive
end,
setScrollable = function(self, scroll)
scrollable = scroll
if(scroll==nil)then scrollable = true end
self:updateDraw()
return self
end,
getScrollable = function(self)
return scrollable
end,
scrollHandler = function(self, dir, x, y)
if(base.scrollHandler(self, dir, x, y))then
if(scrollable)then
local w,h = self:getSize()
yOffset = yOffset + dir
if (yOffset < 0) then
yOffset = 0
end
if (dir >= 1) then
if (#list > h) then
if (yOffset > #list - h) then
yOffset = #list - h
end
if (yOffset >= #list) then
yOffset = #list - 1
end
else
yOffset = yOffset - 1
end
end
self:updateDraw()
end
return true
end
return false
end,
mouseHandler = function(self, button, x, y)
if(base.mouseHandler(self, button, x, y))then
local obx, oby = self:getAbsolutePosition()
local w,h = self:getSize()
if (#list > 0) then
for n = 1, h do
if (list[n + yOffset] ~= nil) then
if (obx <= x) and (obx + w > x) and (oby + n - 1 == y) then
self:setValue(list[n + yOffset])
self:selectHandler()
self:updateDraw()
end
end
end
end
return true
end
return false
end,
dragHandler = function(self, button, x, y)
return self:mouseHandler(button, x, y)
end,
touchHandler = function(self, x, y)
return self:mouseHandler(1, x, y)
end,
onSelect = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("select_item", v)
end
end
return self
end,
selectHandler = function(self)
self:sendEvent("select_item", self:getValue())
end,
draw = function(self)
base.draw(self)
self:addDraw("list", function()
local w, h = self:getSize()
for n = 1, h do
if list[n + yOffset] then
local t = list[n + yOffset].text
local fg, bg = list[n + yOffset].fgCol, list[n + yOffset].bgCol
if list[n + yOffset] == self:getValue() and selectionColorActive then
fg, bg = itemSelectedFG, itemSelectedBG
end
self:addText(1, n, t:sub(1,w))
self:addBG(1, n, tHex[bg]:rep(w))
self:addFG(1, n, tHex[fg]:rep(w))
end
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,131 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("List")(name, basalt)
local objectType = "Menubar"
local object = {}
base:setSize(30, 1)
base:setZIndex(5)
local itemOffset = 0
local space, outerSpace = 1, 1
local scrollable = true
local function maxScroll()
local mScroll = 0
local w = base:getWidth()
local list = base:getAll()
for n = 1, #list do
mScroll = mScroll + list[n].text:len() + space * 2
end
return math.max(mScroll - w, 0)
end
object = {
init = function(self)
local parent = self:getParent()
self:listenEvent("mouse_click")
self:listenEvent("mouse_drag")
self:listenEvent("mouse_scroll")
return base.init(self)
end,
getType = function(self)
return objectType
end,
getBase = function(self)
return base
end,
setSpace = function(self, _space)
space = _space or space
self:updateDraw()
return self
end,
getSpace = function(self)
return space
end,
setScrollable = function(self, scroll)
scrollable = scroll
if(scroll==nil)then scrollable = true end
return self
end,
getScrollable = function(self)
return scrollable
end,
mouseHandler = function(self, button, x, y)
if(base:getBase().mouseHandler(self, button, x, y))then
local objX, objY = self:getAbsolutePosition()
local w,h = self:getSize()
local xPos = 0
local list = self:getAll()
for n = 1, #list do
if (list[n] ~= nil) then
if (objX + xPos <= x + itemOffset) and (objX + xPos + list[n].text:len() + (space*2) > x + itemOffset) and (objY == y) then
self:setValue(list[n])
self:sendEvent(event, self, event, 0, x, y, list[n])
end
xPos = xPos + list[n].text:len() + space * 2
end
end
self:updateDraw()
return true
end
end,
scrollHandler = function(self, dir, x, y)
if(base:getBase().scrollHandler(self, dir, x, y))then
if(scrollable)then
itemOffset = itemOffset + dir
if (itemOffset < 0) then
itemOffset = 0
end
local mScroll = maxScroll()
if (itemOffset > mScroll) then
itemOffset = mScroll
end
self:updateDraw()
end
return true
end
return false
end,
draw = function(self)
base.draw(self)
self:addDraw("list", function()
local parent = self:getParent()
local w,h = self:getSize()
local text = ""
local textBGCol = ""
local textFGCol = ""
local itemSelectedBG, itemSelectedFG = self:getSelectionColor()
for _, v in pairs(self:getAll()) do
local newItem = (" "):rep(space) .. v.text .. (" "):rep(space)
text = text .. newItem
if(v == self:getValue())then
textBGCol = textBGCol .. tHex[itemSelectedBG or v.bgCol or self:getBackground()]:rep(newItem:len())
textFGCol = textFGCol .. tHex[itemSelectedFG or v.FgCol or self:getForeground()]:rep(newItem:len())
else
textBGCol = textBGCol .. tHex[v.bgCol or self:getBackground()]:rep(newItem:len())
textFGCol = textFGCol .. tHex[v.FgCol or self:getForeground()]:rep(newItem:len())
end
end
self:addBlit(1, 1, text:sub(itemOffset+1, w+itemOffset), textFGCol:sub(itemOffset+1, w+itemOffset), textBGCol:sub(itemOffset+1, w+itemOffset))
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,81 @@
local basaltMon = require("basaltMon")
local max,min,sub,rep = math.max,math.min,string.sub,string.rep
return function(name, basalt)
local base = basalt.getObject("BaseFrame")(name, basalt)
local objectType = "MonitorFrame"
base:setTerm(nil)
local isMonitorGroup = false
local monGroup
local object = {
getType = function()
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
getBase = function(self)
return base
end,
setMonitor = function(self, newMon)
if(type(newMon)=="string")then
local mon = peripheral.wrap(newMon)
if(mon~=nil)then
self:setTerm(mon)
end
elseif(type(newMon)=="table")then
self:setTerm(newMon)
end
return self
end,
setMonitorGroup = function(self, monGrp)
monGroup = basaltMon(monGrp)
self:setTerm(monGroup)
isMonitorGroup = true
return self
end,
render = function(self)
if(self:getTerm()~=nil)then
base.render(self)
end
end,
show = function(self)
base:getBase().show(self)
basalt.setActiveFrame(self)
for k,v in pairs(colors)do
if(type(v)=="number")then
termObject.setPaletteColor(v, colors.packRGB(term.nativePaletteColor((v))))
end
end
for k,v in pairs(colorTheme)do
if(type(v)=="number")then
termObject.setPaletteColor(type(k)=="number" and k or colors[k], v)
else
local r,g,b = table.unpack(v)
termObject.setPaletteColor(type(k)=="number" and k or colors[k], r,g,b)
end
end
return self
end,
}
object.mouseHandler = function(self, btn, x, y, isMon, monitor, ...)
if(isMonitorGroup)then
x, y = monGroup.calculateClick(monitor, x, y)
end
base.mouseHandler(self, btn, x, y, isMon, monitor, ...)
end
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,99 @@
local max,min,sub,rep = math.max,math.min,string.sub,string.rep
return function(name, basalt)
local base = basalt.getObject("Frame")(name, basalt)
local objectType = "MovableFrame"
local parent
local dragXOffset, dragYOffset, isDragging = 0, 0, false
local dragMap = {
{x1 = 1, x2 = "width", y1 = 1, y2 = 1}
}
local object = {
getType = function()
return objectType
end,
setDraggingMap = function(self, t)
dragMap = t
return self
end,
getDraggingMap = function(self)
return dragMap
end,
isType = function(self, t)
return objectType==t or (base.isType~=nil and base.isType(t)) or false
end,
getBase = function(self)
return base
end,
load = function(self)
base.load(self)
self:listenEvent("mouse_click")
self:listenEvent("mouse_up")
self:listenEvent("mouse_drag")
end,
removeChildren = function(self)
base.removeChildren(self)
self:listenEvent("mouse_click")
self:listenEvent("mouse_up")
self:listenEvent("mouse_drag")
end,
dragHandler = function(self, btn, x, y)
if(base.dragHandler(self, btn, x, y))then
if (isDragging) then
local xO, yO = parent:getOffset()
xO = xO < 0 and math.abs(xO) or -xO
yO = yO < 0 and math.abs(yO) or -yO
local parentX = 1
local parentY = 1
parentX, parentY = parent:getAbsolutePosition()
self:setPosition(x + dragXOffset - (parentX - 1) + xO, y + dragYOffset - (parentY - 1) + yO)
self:updateDraw()
end
return true
end
end,
mouseHandler = function(self, btn, x, y, ...)
if(base.mouseHandler(self, btn, x, y, ...))then
parent:setImportant(self)
local fx, fy = self:getAbsolutePosition()
local w, h = self:getSize()
for k,v in pairs(dragMap)do
local x1, x2 = v.x1=="width" and w or v.x1, v.x2=="width" and w or v.x2
local y1, y2= v.y1=="height" and h or v.y1, v.y2=="height" and h or v.y2
if(x>=fx+x1-1)and(x<=fx+x2-1)and(y>=fy+y1-1)and(y<=fy+y2-1)then
isDragging = true
dragXOffset = fx - x
dragYOffset = fy - y
return true
end
end
return true
end
end,
mouseUpHandler = function(self, ...)
isDragging = false
return base.mouseUpHandler(self, ...)
end,
setParent = function(self, p, ...)
base.setParent(self, p, ...)
parent = p
return self
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,283 @@
local basaltEvent = require("basaltEvent")
local utils = require("utils")
local uuid = utils.uuid
local unpack,sub = table.unpack,string.sub
return function(name, basalt)
name = name or uuid()
assert(basalt~=nil, "Unable to find basalt instance! ID: "..name)
-- Base object
local objectType = "Object" -- not changeable
local isEnabled,initialized = true,false
local eventSystem = basaltEvent()
local registeredEvents = {}
local activeEvents = {}
local parent
local object = {
init = function(self)
if(initialized)then return false end
initialized = true
return true
end,
load = function(self)
end,
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType==t
end,
getProperty = function(self, name)
local get = self["get" .. name:gsub("^%l", string.upper)]
if (get ~= nil) then
return get(self)
end
end,
setProperty = function(self, name, ...)
local set = self["set" .. name:gsub("^%l", string.upper)]
if (set ~= nil) then
return set(self, ...)
end
end,
getName = function(self)
return name
end,
getParent = function(self)
return parent
end,
setParent = function(self, newParent, noRemove)
if(noRemove)then parent = newParent return self end
if (newParent.getType ~= nil and newParent:isType("Container")) then
self:remove()
newParent:addChild(self)
if (self.show) then
self:show()
end
parent = newParent
end
return self
end,
updateEvents = function(self)
for k,v in pairs(activeEvents)do
parent:removeEvent(k, self)
if(v)then
parent:addEvent(k, self)
end
end
return self
end,
listenEvent = function(self, event, active)
if(parent~=nil)then
if(active)or(active==nil)then
activeEvents[event] = true
parent:addEvent(event, self)
elseif(active==false)then
activeEvents[event] = false
parent:removeEvent(event, self)
end
end
return self
end,
getZIndex = function(self)
return 1
end,
enable = function(self)
isEnabled = true
return self
end,
disable = function(self)
isEnabled = false
return self
end,
isEnabled = function(self)
return isEnabled
end,
remove = function(self)
if (parent ~= nil) then
parent:removeChild(self)
end
self:updateDraw()
return self
end,
getBaseFrame = function(self)
if(parent~=nil)then
return parent:getBaseFrame()
end
return self
end,
onEvent = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("other_event", v)
end
end
return self
end,
getEventSystem = function(self)
return eventSystem
end,
getRegisteredEvents = function(self)
return registeredEvents
end,
registerEvent = function(self, event, func)
if(parent~=nil)then
parent:addEvent(event, self)
end
eventSystem:registerEvent(event, func)
if (registeredEvents[event] == nil) then
registeredEvents[event] = {}
end
table.insert(registeredEvents[event], func)
end,
removeEvent = function(self, event, index)
if(eventSystem:getEventCount(event)<1)then
if(parent~=nil)then
parent:removeEvent(event, self)
end
end
eventSystem:removeEvent(event, index)
if (registeredEvents[event] ~= nil) then
table.remove(registeredEvents[event], index)
if (#registeredEvents[event] == 0) then
registeredEvents[event] = nil
end
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, ...)
if(event=="other_event")or(event=="custom_event")then
return eventSystem:sendEvent(event, self, ...)
end
return eventSystem:sendEvent(event, self, event, ...)
end,
onClick = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_click", v)
end
end
return self
end,
onClickUp = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_up", v)
end
end
return self
end,
onRelease = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_release", v)
end
end
return self
end,
onScroll = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_scroll", v)
end
end
return self
end,
onHover = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_hover", v)
end
end
return self
end,
onLeave = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_leave", v)
end
end
return self
end,
onDrag = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("mouse_drag", v)
end
end
return self
end,
onKey = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("key", v)
end
end
return self
end,
onChar = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("char", v)
end
end
return self
end,
onKeyUp = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("key_up", v)
end
end
return self
end,
}
object.__index = object
return object
end

View File

@@ -0,0 +1,16 @@
return function(name, basalt)
-- Pane
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Pane"
base:setSize(25, 10)
local object = {
getType = function(self)
return objectType
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,721 @@
local tHex = require("tHex")
local process = require("process")
local sub = string.sub
return function(name, basalt)
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Program"
local object
local cachedPath
local enviroment = {}
local function createBasaltWindow(x, y, width, height)
local xCursor, yCursor = 1, 1
local bgColor, fgColor = colors.black, colors.white
local cursorBlink = false
local visible = false
local cacheT = {}
local cacheBG = {}
local cacheFG = {}
local tPalette = {}
local emptySpaceLine
local emptyColorLines = {}
for i = 0, 15 do
local c = 2 ^ i
tPalette[c] = { basalt.getTerm().getPaletteColour(c) }
end
local function createEmptyLines()
emptySpaceLine = (" "):rep(width)
for n = 0, 15 do
local nColor = 2 ^ n
local sHex = tHex[nColor]
emptyColorLines[nColor] = sHex:rep(width)
end
end
local function recreateWindowArray()
createEmptyLines()
local emptyText = emptySpaceLine
local emptyFG = emptyColorLines[colors.white]
local emptyBG = emptyColorLines[colors.black]
for n = 1, height do
cacheT[n] = sub(cacheT[n] == nil and emptyText or cacheT[n] .. emptyText:sub(1, width - cacheT[n]:len()), 1, width)
cacheFG[n] = sub(cacheFG[n] == nil and emptyFG or cacheFG[n] .. emptyFG:sub(1, width - cacheFG[n]:len()), 1, width)
cacheBG[n] = sub(cacheBG[n] == nil and emptyBG or cacheBG[n] .. emptyBG:sub(1, width - cacheBG[n]:len()), 1, width)
end
base.updateDraw(base)
end
recreateWindowArray()
local function updateCursor()
if xCursor >= 1 and yCursor >= 1 and xCursor <= width and yCursor <= height then
--parentTerminal.setCursorPos(xCursor + x - 1, yCursor + y - 1)
else
--parentTerminal.setCursorPos(0, 0)
end
--parentTerminal.setTextColor(fgColor)
end
local function internalBlit(sText, sTextColor, sBackgroundColor)
if yCursor < 1 or yCursor > height or xCursor < 1 or xCursor + #sText - 1 > width then
return
end
cacheT[yCursor] = sub(cacheT[yCursor], 1, xCursor - 1) .. sText .. sub(cacheT[yCursor], xCursor + #sText, width)
cacheFG[yCursor] = sub(cacheFG[yCursor], 1, xCursor - 1) .. sTextColor .. sub(cacheFG[yCursor], xCursor + #sText, width)
cacheBG[yCursor] = sub(cacheBG[yCursor], 1, xCursor - 1) .. sBackgroundColor .. sub(cacheBG[yCursor], xCursor + #sText, width)
xCursor = xCursor + #sText
if visible then
updateCursor()
end
object:updateDraw()
end
local function setText(_x, _y, text)
if (text ~= nil) then
local gText = cacheT[_y]
if (gText ~= nil) then
cacheT[_y] = sub(gText:sub(1, _x - 1) .. text .. gText:sub(_x + (text:len()), width), 1, width)
end
end
object:updateDraw()
end
local function setBG(_x, _y, colorStr)
if (colorStr ~= nil) then
local gBG = cacheBG[_y]
if (gBG ~= nil) then
cacheBG[_y] = sub(gBG:sub(1, _x - 1) .. colorStr .. gBG:sub(_x + (colorStr:len()), width), 1, width)
end
end
object:updateDraw()
end
local function setFG(_x, _y, colorStr)
if (colorStr ~= nil) then
local gFG = cacheFG[_y]
if (gFG ~= nil) then
cacheFG[_y] = sub(gFG:sub(1, _x - 1) .. colorStr .. gFG:sub(_x + (colorStr:len()), width), 1, width)
end
end
object:updateDraw()
end
local setTextColor = function(color)
if type(color) ~= "number" then
error("bad argument #1 (expected number, got " .. type(color) .. ")", 2)
elseif tHex[color] == nil then
error("Invalid color (got " .. color .. ")", 2)
end
fgColor = color
end
local setBackgroundColor = function(color)
if type(color) ~= "number" then
error("bad argument #1 (expected number, got " .. type(color) .. ")", 2)
elseif tHex[color] == nil then
error("Invalid color (got " .. color .. ")", 2)
end
bgColor = color
end
local setPaletteColor = function(colour, r, g, b)
-- have to work on
if type(colour) ~= "number" then
error("bad argument #1 (expected number, got " .. type(colour) .. ")", 2)
end
if tHex[colour] == nil then
error("Invalid color (got " .. colour .. ")", 2)
end
local tCol
if type(r) == "number" and g == nil and b == nil then
tCol = { colours.rgb8(r) }
tPalette[colour] = tCol
else
if type(r) ~= "number" then
error("bad argument #2 (expected number, got " .. type(r) .. ")", 2)
end
if type(g) ~= "number" then
error("bad argument #3 (expected number, got " .. type(g) .. ")", 2)
end
if type(b) ~= "number" then
error("bad argument #4 (expected number, got " .. type(b) .. ")", 2)
end
tCol = tPalette[colour]
tCol[1] = r
tCol[2] = g
tCol[3] = b
end
end
local getPaletteColor = function(colour)
if type(colour) ~= "number" then
error("bad argument #1 (expected number, got " .. type(colour) .. ")", 2)
end
if tHex[colour] == nil then
error("Invalid color (got " .. colour .. ")", 2)
end
local tCol = tPalette[colour]
return tCol[1], tCol[2], tCol[3]
end
local basaltwindow = {
setCursorPos = function(_x, _y)
if type(_x) ~= "number" then
error("bad argument #1 (expected number, got " .. type(_x) .. ")", 2)
end
if type(_y) ~= "number" then
error("bad argument #2 (expected number, got " .. type(_y) .. ")", 2)
end
xCursor = math.floor(_x)
yCursor = math.floor(_y)
if (visible) then
updateCursor()
end
end,
getCursorPos = function()
return xCursor, yCursor
end;
setCursorBlink = function(blink)
if type(blink) ~= "boolean" then
error("bad argument #1 (expected boolean, got " .. type(blink) .. ")", 2)
end
cursorBlink = blink
end;
getCursorBlink = function()
return cursorBlink
end;
getPaletteColor = getPaletteColor,
getPaletteColour = getPaletteColor,
setBackgroundColor = setBackgroundColor,
setBackgroundColour = setBackgroundColor,
setTextColor = setTextColor,
setTextColour = setTextColor,
setPaletteColor = setPaletteColor,
setPaletteColour = setPaletteColor,
getBackgroundColor = function()
return bgColor
end;
getBackgroundColour = function()
return bgColor
end;
getSize = function()
return width, height
end;
getTextColor = function()
return fgColor
end;
getTextColour = function()
return fgColor
end;
basalt_resize = function(_width, _height)
width, height = _width, _height
recreateWindowArray()
end;
basalt_reposition = function(_x, _y)
x, y = _x, _y
end;
basalt_setVisible = function(vis)
visible = vis
end;
drawBackgroundBox = function(_x, _y, _width, _height, bgCol)
for n = 1, _height do
setBG(_x, _y + (n - 1), tHex[bgCol]:rep(_width))
end
end;
drawForegroundBox = function(_x, _y, _width, _height, fgCol)
for n = 1, _height do
setFG(_x, _y + (n - 1), tHex[fgCol]:rep(_width))
end
end;
drawTextBox = function(_x, _y, _width, _height, symbol)
for n = 1, _height do
setText(_x, _y + (n - 1), symbol:rep(_width))
end
end;
basalt_update = function()
for n = 1, height do
object:addBlit(1, n, cacheT[n], cacheFG[n], cacheBG[n])
end
end,
scroll = function(offset)
assert(type(offset) == "number", "bad argument #1 (expected number, got " .. type(offset) .. ")")
if offset ~= 0 then
for newY = 1, height do
local y = newY + offset
if y < 1 or y > height then
cacheT[newY] = emptySpaceLine
cacheFG[newY] = emptyColorLines[fgColor]
cacheBG[newY] = emptyColorLines[bgColor]
else
cacheT[newY] = cacheT[y]
cacheBG[newY] = cacheBG[y]
cacheFG[newY] = cacheFG[y]
end
end
end
if (visible) then
updateCursor()
end
end,
isColor = function()
return basalt.getTerm().isColor()
end;
isColour = function()
return basalt.getTerm().isColor()
end;
write = function(text)
text = tostring(text)
if (visible) then
internalBlit(text, tHex[fgColor]:rep(text:len()), tHex[bgColor]:rep(text:len()))
end
end;
clearLine = function()
if (visible) then
setText(1, yCursor, (" "):rep(width))
setBG(1, yCursor, tHex[bgColor]:rep(width))
setFG(1, yCursor, tHex[fgColor]:rep(width))
end
if (visible) then
updateCursor()
end
end;
clear = function()
for n = 1, height do
setText(1, n, (" "):rep(width))
setBG(1, n, tHex[bgColor]:rep(width))
setFG(1, n, tHex[fgColor]:rep(width))
end
if (visible) then
updateCursor()
end
end;
blit = function(text, fgcol, bgcol)
if type(text) ~= "string" then
error("bad argument #1 (expected string, got " .. type(text) .. ")", 2)
end
if type(fgcol) ~= "string" then
error("bad argument #2 (expected string, got " .. type(fgcol) .. ")", 2)
end
if type(bgcol) ~= "string" then
error("bad argument #3 (expected string, got " .. type(bgcol) .. ")", 2)
end
if #fgcol ~= #text or #bgcol ~= #text then
error("Arguments must be the same length", 2)
end
if (visible) then
internalBlit(text, fgcol, bgcol)
end
end
}
return basaltwindow
end
base:setZIndex(5)
base:setSize(30, 12)
local pWindow = createBasaltWindow(1, 1, 30, 12)
local curProcess
local paused = false
local queuedEvent = {}
local function updateCursor(self)
local parent = self:getParent()
local xCur, yCur = pWindow.getCursorPos()
local obx, oby = self:getPosition()
local w,h = self:getSize()
if (obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + h - 1) then
parent:setCursor(self:isFocused() and pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor())
end
end
local function resumeProcess(self, event, ...)
local ok, result = curProcess:resume(event, ...)
if (ok==false)and(result~=nil)and(result~="Terminated")then
local val = self:sendEvent("program_error", result)
if(val~=false)then
error("Basalt Program - "..result)
end
end
if(curProcess:getStatus()=="dead")then
self:sendEvent("program_done")
end
end
local function mouseEvent(self, event, p1, x, y)
if (curProcess == nil) then
return false
end
if not (curProcess:isDead()) then
if not (paused) then
local absX, absY = self:getAbsolutePosition()
resumeProcess(self, event, p1, x-absX+1, y-absY+1)
updateCursor(self)
end
end
end
local function keyEvent(self, event, key, isHolding)
if (curProcess == nil) then
return false
end
if not (curProcess:isDead()) then
if not (paused) then
if (self.draw) then
resumeProcess(self, event, key, isHolding)
updateCursor(self)
end
end
end
end
object = {
getType = function(self)
return objectType
end;
show = function(self)
base.show(self)
pWindow.setBackgroundColor(self:getBackground())
pWindow.setTextColor(self:getForeground())
pWindow.basalt_setVisible(true)
return self
end;
hide = function(self)
base.hide(self)
pWindow.basalt_setVisible(false)
return self
end;
setPosition = function(self, x, y, rel)
base.setPosition(self, x, y, rel)
pWindow.basalt_reposition(self:getPosition())
return self
end,
getBasaltWindow = function()
return pWindow
end;
getBasaltProcess = function()
return curProcess
end;
setSize = function(self, width, height, rel)
base.setSize(self, width, height, rel)
pWindow.basalt_resize(self:getWidth(), self:getHeight())
return self
end;
getStatus = function(self)
if (curProcess ~= nil) then
return curProcess:getStatus()
end
return "inactive"
end;
setEnviroment = function(self, env)
enviroment = env or {}
return self
end,
execute = function(self, path, ...)
cachedPath = path or cachedPath
curProcess = process:new(cachedPath, pWindow, enviroment, ...)
pWindow.setBackgroundColor(colors.black)
pWindow.setTextColor(colors.white)
pWindow.clear()
pWindow.setCursorPos(1, 1)
pWindow.setBackgroundColor(self:getBackground())
pWindow.setTextColor(self:getForeground() or colors.white)
pWindow.basalt_setVisible(true)
resumeProcess(self)
paused = false
self:listenEvent("mouse_click", self)
self:listenEvent("mouse_up", self)
self:listenEvent("mouse_drag", self)
self:listenEvent("mouse_scroll", self)
self:listenEvent("key", self)
self:listenEvent("key_up", self)
self:listenEvent("char", self)
self:listenEvent("other_event", self)
return self
end;
setExecute = function(self, path, ...)
return self:execute(path, ...)
end,
stop = function(self)
local parent = self:getParent()
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
resumeProcess(self, "terminate")
if (curProcess:isDead()) then
parent:setCursor(false)
end
end
end
parent:removeEvents(self)
return self
end;
pause = function(self, p)
paused = p or (not paused)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
if not (paused) then
self:injectEvents(table.unpack(queuedEvent))
queuedEvent = {}
end
end
end
return self
end;
isPaused = function(self)
return paused
end;
injectEvent = function(self, event, ign, ...)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
if (paused == false) or (ign) then
resumeProcess(self, event, ...)
else
table.insert(queuedEvent, { event = event, args = {...} })
end
end
end
return self
end;
getQueuedEvents = function(self)
return queuedEvent
end;
updateQueuedEvents = function(self, events)
queuedEvent = events or queuedEvent
return self
end;
injectEvents = function(self, ...)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
for _, value in pairs({...}) do
resumeProcess(self, value.event, table.unpack(value.args))
end
end
end
return self
end;
mouseHandler = function(self, button, x, y)
if (base.mouseHandler(self, button, x, y)) then
mouseEvent(self, "mouse_click", button, x, y)
return true
end
return false
end,
mouseUpHandler = function(self, button, x, y)
if (base.mouseUpHandler(self, button, x, y)) then
mouseEvent(self, "mouse_up", button, x, y)
return true
end
return false
end,
scrollHandler = function(self, dir, x, y)
if (base.scrollHandler(self, dir, x, y)) then
mouseEvent(self, "mouse_scroll", dir, x, y)
return true
end
return false
end,
dragHandler = function(self, button, x, y)
if (base.dragHandler(self, button, x, y)) then
mouseEvent(self, "mouse_drag", button, x, y)
return true
end
return false
end,
keyHandler = function(self, key, isHolding)
if(base.keyHandler(self, key, isHolding))then
keyEvent(self, "key", key, isHolding)
return true
end
return false
end,
keyUpHandler = function(self, key)
if(base.keyUpHandler(self, key))then
keyEvent(self, "key_up", key)
return true
end
return false
end,
charHandler = function(self, char)
if(base.charHandler(self, char))then
keyEvent(self, "char", char)
return true
end
return false
end,
getFocusHandler = function(self)
base.getFocusHandler(self)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
if not (paused) then
local parent = self:getParent()
if (parent ~= nil) then
local xCur, yCur = pWindow.getCursorPos()
local obx, oby = self:getPosition()
local w,h = self:getSize()
if (obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + h - 1) then
parent:setCursor(pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor())
end
end
end
end
end
end,
loseFocusHandler = function(self)
base.loseFocusHandler(self)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
local parent = self:getParent()
if (parent ~= nil) then
parent:setCursor(false)
end
end
end
end,
eventHandler = function(self, event, ...)
base.eventHandler(self, event, ...)
if curProcess == nil then
return
end
if not curProcess:isDead() then
if not paused then
resumeProcess(self, event, ...)
if self:isFocused() then
local parent = self:getParent()
local obx, oby = self:getPosition()
local xCur, yCur = pWindow.getCursorPos()
local w,h = self:getSize()
if obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + h - 1 then
parent:setCursor(pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor())
end
end
else
table.insert(queuedEvent, { event = event, args = { ... } })
end
end
end,
resizeHandler = function(self, ...)
base.resizeHandler(self, ...)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
if not (paused) then
pWindow.basalt_resize(self:getSize())
resumeProcess(self, "term_resize", self:getSize())
else
pWindow.basalt_resize(self:getSize())
table.insert(queuedEvent, { event = "term_resize", args = { self:getSize() } })
end
end
end
end,
repositionHandler = function(self, ...)
base.repositionHandler(self, ...)
if (curProcess ~= nil) then
if not (curProcess:isDead()) then
pWindow.basalt_reposition(self:getPosition())
end
end
end,
draw = function(self)
base.draw(self)
self:addDraw("program", function()
local parent = self:getParent()
local obx, oby = self:getPosition()
local xCur, yCur = pWindow.getCursorPos()
local w,h = self:getSize()
pWindow.basalt_update()
end)
end,
onError = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("program_error", v)
end
end
local parent = self:getParent()
self:listenEvent("other_event")
return self
end,
onDone = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("program_done", v)
end
end
local parent = self:getParent()
self:listenEvent("other_event")
return self
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,136 @@
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Progressbar"
local progress = 0
base:setZIndex(5)
base:setValue(false)
base:setSize(25, 3)
local activeBarColor = colors.black
local activeBarSymbol = ""
local activeBarSymbolCol = colors.white
local bgBarSymbol = ""
local direction = 0
local object = {
getType = function(self)
return objectType
end,
setDirection = function(self, dir)
direction = dir
self:updateDraw()
return self
end,
getDirection = function(self)
return direction
end,
setProgressBar = function(self, color, symbol, symbolcolor)
activeBarColor = color or activeBarColor
activeBarSymbol = symbol or activeBarSymbol
activeBarSymbolCol = symbolcolor or activeBarSymbolCol
self:updateDraw()
return self
end,
getProgressBar = function(self)
return activeBarColor, activeBarSymbol, activeBarSymbolCol
end,
setActiveBarColor = function(self, color)
return self:setProgressBar(color, nil, nil)
end,
getActiveBarColor = function(self)
return activeBarColor
end,
setActiveBarSymbol = function(self, symbol)
return self:setProgressBar(nil, symbol, nil)
end,
getActiveBarSymbol = function(self)
return activeBarSymbol
end,
setActiveBarSymbolColor = function(self, symbolColor)
return self:setProgressBar(nil, nil, symbolColor)
end,
getActiveBarSymbolColor = function(self)
return activeBarSymbolCol
end,
setBackgroundSymbol = function(self, symbol)
bgBarSymbol = symbol:sub(1, 1)
self:updateDraw()
return self
end,
getBackgroundSymbol = function(self)
return bgBarSymbol
end,
setProgress = function(self, value)
if (value >= 0) and (value <= 100) and (progress ~= value) then
progress = value
self:setValue(progress)
if (progress == 100) then
self:progressDoneHandler()
end
end
self:updateDraw()
return self
end,
getProgress = function(self)
return progress
end,
onProgressDone = function(self, f)
self:registerEvent("progress_done", f)
return self
end,
progressDoneHandler = function(self)
self:sendEvent("progress_done")
end,
draw = function(self)
base.draw(self)
self:addDraw("progressbar", function()
local obx, oby = self:getPosition()
local w,h = self:getSize()
local bgCol,fgCol = self:getBackground(), self:getForeground()
if(bgCol~=false)then self:addBackgroundBox(1, 1, w, h, bgCol) end
if(bgBarSymbol~="")then self:addTextBox(1, 1, w, h, bgBarSymbol) end
if(fgCol~=false)then self:addForegroundBox(1, 1, w, h, fgCol) end
if (direction == 1) then
self:addBackgroundBox(1, 1, w, h / 100 * progress, activeBarColor)
self:addForegroundBox(1, 1, w, h / 100 * progress, activeBarSymbolCol)
self:addTextBox(1, 1, w, h / 100 * progress, activeBarSymbol)
elseif (direction == 3) then
self:addBackgroundBox(1, 1 + math.ceil(h - h / 100 * progress), w, h / 100 * progress, activeBarColor)
self:addForegroundBox(1, 1 + math.ceil(h - h / 100 * progress), w, h / 100 * progress, activeBarSymbolCol)
self:addTextBox(1, 1 + math.ceil(h - h / 100 * progress), w, h / 100 * progress, activeBarSymbol)
elseif (direction == 2) then
self:addBackgroundBox(1 + math.ceil(w - w / 100 * progress), 1, w / 100 * progress, h, activeBarColor)
self:addForegroundBox(1 + math.ceil(w - w / 100 * progress), 1, w / 100 * progress, h, activeBarSymbolCol)
self:addTextBox(1 + math.ceil(w - w / 100 * progress), 1, w / 100 * progress, h, activeBarSymbol)
else
self:addBackgroundBox(1, 1, math.ceil( w / 100 * progress), h, activeBarColor)
self:addForegroundBox(1, 1, math.ceil(w / 100 * progress), h, activeBarSymbolCol)
self:addTextBox(1, 1, math.ceil(w / 100 * progress), h, activeBarSymbol)
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,138 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("List")(name, basalt)
local objectType = "Radio"
base:setSize(1, 1)
base:setZIndex(5)
local list = {}
local boxSelectedBG = colors.black
local boxSelectedFG = colors.green
local boxNotSelectedBG = colors.black
local boxNotSelectedFG = colors.red
local selectionColorActive = true
local symbol = "\7"
local align = "left"
local object = {
getType = function(self)
return objectType
end,
addItem = function(self, text, x, y, bgCol, fgCol, ...)
base.addItem(self, text, bgCol, fgCol, ...)
table.insert(list, { x = x or 1, y = y or #list * 2 })
return self
end,
removeItem = function(self, index)
base.removeItem(self, index)
table.remove(list, index)
return self
end,
clear = function(self)
base.clear(self)
list = {}
return self
end,
editItem = function(self, index, text, x, y, bgCol, fgCol, ...)
base.editItem(self, index, text, bgCol, fgCol, ...)
table.remove(list, index)
table.insert(list, index, { x = x or 1, y = y or 1 })
return self
end,
setBoxSelectionColor = function(self, bg, fg)
boxSelectedBG = bg
boxSelectedFG = fg
return self
end,
setBoxSelectionBG = function(self, bg)
return self:setBoxSelectionColor(bg, boxSelectedFG)
end,
setBoxSelectionFG = function(self, fg)
return self:setBoxSelectionColor(boxSelectedBG, fg)
end,
getBoxSelectionColor = function(self)
return boxSelectedBG, boxSelectedFG
end,
getBoxSelectionBG = function(self)
return boxSelectedBG
end,
getBoxSelectionFG = function(self)
return boxSelectedFG
end,
setBoxDefaultColor = function(self, bg, fg)
boxNotSelectedBG = bg
boxNotSelectedFG = fg
return self
end,
setBoxDefaultBG = function(self, bg)
return self:setBoxDefaultColor(bg, boxNotSelectedFG)
end,
setBoxDefaultFG = function(self, fg)
return self:setBoxDefaultColor(boxNotSelectedBG, fg)
end,
getBoxDefaultColor = function(self)
return boxNotSelectedBG, boxNotSelectedFG
end,
getBoxDefaultBG = function(self)
return boxNotSelectedBG
end,
getBoxDefaultFG = function(self)
return boxNotSelectedFG
end,
mouseHandler = function(self, button, x, y, ...)
if (#list > 0) then
local obx, oby = self:getAbsolutePosition()
local baseList = self:getAll()
for k, value in pairs(baseList) do
if (obx + list[k].x - 1 <= x) and (obx + list[k].x - 1 + value.text:len() + 1 >= x) and (oby + list[k].y - 1 == y) then
self:setValue(value)
local val = self:sendEvent("mouse_click", self, "mouse_click", button, x, y, ...)
self:updateDraw()
if(val==false)then return val end
return true
end
end
end
end,
draw = function(self)
self:addDraw("radio", function()
local itemSelectedBG, itemSelectedFG = self:getSelectionColor()
local baseList = self:getAll()
for k, value in pairs(baseList) do
if (value == self:getValue()) then
self:addBlit(list[k].x, list[k].y, symbol, tHex[boxSelectedFG], tHex[boxSelectedBG])
self:addBlit(list[k].x + 2, list[k].y, value.text, tHex[itemSelectedFG]:rep(#value.text), tHex[itemSelectedBG]:rep(#value.text))
else
self:addBackgroundBox(list[k].x, list[k].y, 1, 1, boxNotSelectedBG or colors.black)
self:addBlit(list[k].x + 2, list[k].y, value.text, tHex[value.fgCol]:rep(#value.text), tHex[value.bgCol]:rep(#value.text))
end
end
return true
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,147 @@
local max,min,sub,rep = math.max,math.min,string.sub,string.rep
return function(name, basalt)
local base = basalt.getObject("Frame")(name, basalt)
local objectType = "ScrollableFrame"
local parent
local direction = 0
local manualScrollAmount = 0
local calculateScrollAmount = true
local function getHorizontalScrollAmount(self)
local amount = 0
local children = self:getChildren()
for _, b in pairs(children) do
if(b.element.getWidth~=nil)and(b.element.getX~=nil)then
local w, x = b.element:getWidth(), b.element:getX()
local width = self:getWidth()
if(b.element:getType()=="Dropdown")then
if(b.element:isOpened())then
local dropdownW = b.element:getDropdownSize()
if (dropdownW + x - width >= amount) then
amount = max(dropdownW + x - width, 0)
end
end
end
if (w + x - width >= amount) then
amount = max(w + x - width, 0)
end
end
end
return amount
end
local function getVerticalScrollAmount(self)
local amount = 0
local children = self:getChildren()
for _, b in pairs(children) do
if(b.element.getHeight~=nil)and(b.element.getY~=nil)then
local h, y = b.element:getHeight(), b.element:getY()
local height = self:getHeight()
if(b.element:getType()=="Dropdown")then
if(b.element:isOpened())then
local _,dropdownH = b.element:getDropdownSize()
if (dropdownH + y - height >= amount) then
amount = max(dropdownH + y - height, 0)
end
end
end
if (h + y - height >= amount) then
amount = max(h + y - height, 0)
end
end
end
return amount
end
local function scrollHandler(self, dir)
local xO, yO = self:getOffset()
local scrollAmn
if(direction==1)then
scrollAmn = calculateScrollAmount and getHorizontalScrollAmount(self) or manualScrollAmount
self:setOffset(min(scrollAmn, max(0, xO + dir)), yO)
elseif(direction==0)then
scrollAmn = calculateScrollAmount and getVerticalScrollAmount(self) or manualScrollAmount
self:setOffset(xO, min(scrollAmn, max(0, yO + dir)))
end
self:updateDraw()
end
local object = {
getType = function()
return objectType
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
setDirection = function(self, dir)
direction = dir=="horizontal" and 1 or dir=="vertical" and 0 or direction
return self
end,
setScrollAmount = function(self, amount)
manualScrollAmount = amount
calculateScrollAmount = false
return self
end,
getBase = function(self)
return base
end,
load = function(self)
base.load(self)
self:listenEvent("mouse_scroll")
end,
removeChildren = function(self)
base.removeChildren(self)
self:listenEvent("mouse_scroll")
end,
setParent = function(self, p, ...)
base.setParent(self, p, ...)
parent = p
return self
end,
scrollHandler = function(self, dir, x, y)
if(base:getBase().scrollHandler(self, dir, x, y))then
self:sortChildren()
for _, obj in ipairs(self:getEvents("mouse_scroll")) do
if (obj.element.scrollHandler ~= nil) then
local xO, yO = 0, 0
if(self.getOffset~=nil)then
xO, yO = self:getOffset()
end
if(obj.element.getIgnoreOffset())then
xO, yO = 0, 0
end
if (obj.element.scrollHandler(obj.element, dir, x+xO, y+yO)) then
return true
end
end
end
scrollHandler(self, dir, x, y)
self:clearFocusedChild()
return true
end
end,
draw = function(self)
base.draw(self)
self:addDraw("scrollableFrame", function()
if(calculateScrollAmount)then
scrollHandler(self, 0)
end
end, 0)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,218 @@
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("VisualObject")(name, basalt)
local objectType = "Scrollbar"
base:setZIndex(2)
base:setSize(1, 8)
base:setBackground(colors.lightGray, "\127", colors.gray)
local barType = "vertical"
local symbol = " "
local symbolBG = colors.black
local symbolFG = colors.black
local scrollAmount = 3
local index = 1
local symbolSize = 1
local symbolAutoSize = true
local function updateSymbolSize()
local w,h = base:getSize()
if(symbolAutoSize)then
symbolSize = math.max((barType == "vertical" and h or w-(#symbol)) - (scrollAmount-1), 1)
end
end
updateSymbolSize()
local function mouseEvent(self, button, x, y)
local obx, oby = self:getAbsolutePosition()
local w,h = self:getSize()
updateSymbolSize()
local size = barType == "vertical" and h or w
for i = 0, size do
if ((barType == "vertical" and oby + i == y) or (barType == "horizontal" and obx + i == x)) and (obx <= x) and (obx + w > x) and (oby <= y) and (oby + h > y) then
index = math.min(i + 1, size - (#symbol + symbolSize - 2))
self:scrollbarMoveHandler()
self:updateDraw()
end
end
end
local object = {
getType = function(self)
return objectType
end,
load = function(self)
base.load(self)
local parent = self:getParent()
self:listenEvent("mouse_click")
self:listenEvent("mouse_up")
self:listenEvent("mouse_scroll")
self:listenEvent("mouse_drag")
end,
setSymbol = function(self, _symbol, bg, fg)
symbol = _symbol:sub(1,1)
symbolBG = bg or symbolBG
symbolFG = fg or symbolFG
updateSymbolSize()
self:updateDraw()
return self
end,
setSymbolBG = function(self, bg)
return self:setSymbol(symbol, bg, nil)
end,
setSymbolFG = function(self, fg)
return self:setSymbol(symbol, nil, fg)
end,
getSymbol = function(self)
return symbol
end,
getSymbolBG = function(self)
return symbolBG
end,
getSymbolFG = function(self)
return symbolFG
end,
setIndex = function(self, _index)
index = _index
if (index < 1) then
index = 1
end
local w,h = self:getSize()
--index = math.min(index, (barType == "vertical" and h or w) - (symbolSize - 1))
updateSymbolSize()
self:updateDraw()
return self
end,
setScrollAmount = function(self, amount)
scrollAmount = amount
updateSymbolSize()
self:updateDraw()
return self
end,
getScrollAmount = function(self)
return scrollAmount
end,
getIndex = function(self)
local w,h = self:getSize()
return scrollAmount > (barType=="vertical" and h or w) and math.floor(scrollAmount/(barType=="vertical" and h or w) * index) or index
end,
setSymbolSize = function(self, size)
symbolSize = tonumber(size) or 1
symbolAutoSize = size~=false and false or true
updateSymbolSize()
self:updateDraw()
return self
end,
getSymbolSize = function(self)
return symbolSize
end,
setBarType = function(self, _typ)
barType = _typ:lower()
updateSymbolSize()
self:updateDraw()
return self
end,
getBarType = function(self)
return barType
end,
mouseHandler = function(self, button, x, y, ...)
if (base.mouseHandler(self, button, x, y, ...)) then
mouseEvent(self, button, x, y)
return true
end
return false
end,
dragHandler = function(self, button, x, y)
if (base.dragHandler(self, button, x, y)) then
mouseEvent(self, button, x, y)
return true
end
return false
end,
setSize = function(self, ...)
base.setSize(self, ...)
updateSymbolSize()
return self
end,
scrollHandler = function(self, dir, x, y)
if(base.scrollHandler(self, dir, x, y))then
local w,h = self:getSize()
updateSymbolSize()
index = index + dir
if (index < 1) then
index = 1
end
index = math.min(index, (barType == "vertical" and h or w) - (barType == "vertical" and symbolSize - 1 or #symbol+symbolSize-2))
self:scrollbarMoveHandler()
self:updateDraw()
end
end,
onChange = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("scrollbar_moved", v)
end
end
return self
end,
scrollbarMoveHandler = function(self)
self:sendEvent("scrollbar_moved", self:getIndex())
end,
customEventHandler = function(self, event, ...)
base.customEventHandler(self, event, ...)
if(event=="basalt_FrameResize")then
updateSymbolSize()
end
end,
draw = function(self)
base.draw(self)
self:addDraw("scrollbar", function()
local parent = self:getParent()
local w,h = self:getSize()
local bgCol,fgCol = self:getBackground(), self:getForeground()
if (barType == "horizontal") then
for n = 0, h - 1 do
self:addBlit(index, 1 + n, symbol:rep(symbolSize), tHex[symbolFG]:rep(#symbol*symbolSize), tHex[symbolBG]:rep(#symbol*symbolSize))
end
elseif (barType == "vertical") then
for n = 0, h - 1 do
if (index == n + 1) then
for curIndexOffset = 0, math.min(symbolSize - 1, h) do
self:addBlit(1, index + curIndexOffset, symbol:rep(math.max(#symbol, w)), tHex[symbolFG]:rep(math.max(#symbol, w)), tHex[symbolBG]:rep(math.max(#symbol, w)))
end
end
end
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,159 @@
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Slider"
base:setSize(12, 1)
base:setValue(1)
base:setBackground(false, "\140", colors.black)
local barType = "horizontal"
local symbol = " "
local symbolFG = colors.black
local symbolBG = colors.gray
local maxValue = 12
local index = 1
local symbolSize = 1
local function mouseEvent(self, button, x, y)
local obx, oby = self:getPosition()
local w,h = self:getSize()
local size = barType == "vertical" and h or w
for i = 0, size do
if ((barType == "vertical" and oby + i == y) or (barType == "horizontal" and obx + i == x)) and (obx <= x) and (obx + w > x) and (oby <= y) and (oby + h > y) then
index = math.min(i + 1, size - (#symbol + symbolSize - 2))
self:setValue(maxValue / size * index)
self:updateDraw()
end
end
end
local object = {
getType = function(self)
return objectType
end,
load = function(self)
self:listenEvent("mouse_click")
self:listenEvent("mouse_drag")
self:listenEvent("mouse_scroll")
end,
setSymbol = function(self, _symbol)
symbol = _symbol:sub(1, 1)
self:updateDraw()
return self
end,
getSymbol = function(self)
return symbol
end,
setIndex = function(self, _index)
index = _index
if (index < 1) then
index = 1
end
local w,h = self:getSize()
index = math.min(index, (barType == "vertical" and h or w) - (symbolSize - 1))
self:setValue(maxValue / (barType == "vertical" and h or w) * index)
self:updateDraw()
return self
end,
getIndex = function(self)
return index
end,
setMaxValue = function(self, val)
maxValue = val
return self
end,
getMaxValue = function(self)
return maxValue
end,
setSymbolColor = function(self, col)
symbolColor = col
self:updateDraw()
return self
end,
getSymbolColor = function(self)
return symbolColor
end,
setBarType = function(self, _typ)
barType = _typ:lower()
self:updateDraw()
return self
end,
getBarType = function(self)
return barType
end,
mouseHandler = function(self, button, x, y)
if (base.mouseHandler(self, button, x, y)) then
mouseEvent(self, button, x, y)
return true
end
return false
end,
dragHandler = function(self, button, x, y)
if (base.dragHandler(self, button, x, y)) then
mouseEvent(self, button, x, y)
return true
end
return false
end,
scrollHandler = function(self, dir, x, y)
if(base.scrollHandler(self, dir, x, y))then
local w,h = self:getSize()
index = index + dir
if (index < 1) then
index = 1
end
index = math.min(index, (barType == "vertical" and h or w) - (symbolSize - 1))
self:setValue(maxValue / (barType == "vertical" and h or w) * index)
self:updateDraw()
return true
end
return false
end,
draw = function(self)
base.draw(self)
self:addDraw("slider", function()
local w,h = self:getSize()
local bgCol,fgCol = self:getBackground(), self:getForeground()
if (barType == "horizontal") then
self:addText(index, oby, symbol:rep(symbolSize))
if(symbolBG~=false)then self:addBG(index, 1, tHex[symbolBG]:rep(#symbol*symbolSize)) end
if(symbolFG~=false)then self:addFG(index, 1, tHex[symbolFG]:rep(#symbol*symbolSize)) end
end
if (barType == "vertical") then
for n = 0, h - 1 do
if (index == n + 1) then
for curIndexOffset = 0, math.min(symbolSize - 1, h) do
self:addBlit(1, 1+n+curIndexOffset, symbol, tHex[symbolColor], tHex[symbolColor])
end
else
if (n + 1 < index) or (n + 1 > index - 1 + symbolSize) then
self:addBlit(1, 1+n, bgSymbol, tHex[fgCol], tHex[bgCol])
end
end
end
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,77 @@
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Switch"
base:setSize(4, 1)
base:setValue(false)
base:setZIndex(5)
local bgSymbol = colors.black
local inactiveBG = colors.red
local activeBG = colors.green
local object = {
getType = function(self)
return objectType
end,
setSymbol = function(self, col)
bgSymbol = col
return self
end,
getSymbol = function(self)
return bgSymbol
end,
setActiveBackground = function(self, col)
activeBG = col
return self
end,
getActiveBackground = function(self)
return activeBG
end,
setInactiveBackground = function(self, col)
inactiveBG = col
return self
end,
getInactiveBackground = function(self)
return inactiveBG
end,
load = function(self)
self:listenEvent("mouse_click")
end,
mouseHandler = function(self, ...)
if (base.mouseHandler(self, ...)) then
self:setValue(not self:getValue())
self:updateDraw()
return true
end
end,
draw = function(self)
base.draw(self)
self:addDraw("switch", function()
local parent = self:getParent()
local bgCol,fgCol = self:getBackground(), self:getForeground()
local w,h = self:getSize()
if(self:getValue())then
self:addBackgroundBox(1, 1, w, h, activeBG)
self:addBackgroundBox(w, 1, 1, h, bgSymbol)
else
self:addBackgroundBox(1, 1, w, h, inactiveBG)
self:addBackgroundBox(1, 1, 1, h, bgSymbol)
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,774 @@
local tHex = require("tHex")
local rep,find,gmatch,sub,len = string.rep,string.find,string.gmatch,string.sub,string.len
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Textfield"
local hIndex, wIndex, textX, textY = 1, 1, 1, 1
local lines = { "" }
local bgLines = { "" }
local fgLines = { "" }
local keyWords = { }
local rules = { }
local startSelX,endSelX,startSelY,endSelY
local selectionBG,selectionFG = colors.lightBlue,colors.black
base:setSize(30, 12)
base:setZIndex(5)
local function isSelected()
if(startSelX~=nil)and(endSelX~=nil)and(startSelY~=nil)and(endSelY~=nil)then
return true
end
return false
end
local function getSelectionCoordinates()
local sx, ex, sy, ey = startSelX, endSelX, startSelY, endSelY
if isSelected() then
if startSelX < endSelX and startSelY <= endSelY then
sx = startSelX
ex = endSelX
if startSelY < endSelY then
sy = startSelY
ey = endSelY
else
sy = endSelY
ey = startSelY
end
elseif startSelX > endSelX and startSelY >= endSelY then
sx = endSelX
ex = startSelX
if startSelY > endSelY then
sy = endSelY
ey = startSelY
else
sy = startSelY
ey = endSelY
end
elseif startSelY > endSelY then
sx = endSelX
ex = startSelX
sy = endSelY
ey = startSelY
end
return sx, ex, sy, ey
end
end
local function removeSelection(self)
local sx, ex, sy, ey = getSelectionCoordinates()
local startLine = lines[sy]
local endLine = lines[ey]
lines[sy] = startLine:sub(1, sx - 1) .. endLine:sub(ex + 1, endLine:len())
bgLines[sy] = bgLines[sy]:sub(1, sx - 1) .. bgLines[ey]:sub(ex + 1, bgLines[ey]:len())
fgLines[sy] = fgLines[sy]:sub(1, sx - 1) .. fgLines[ey]:sub(ex + 1, fgLines[ey]:len())
for i = ey, sy + 1, -1 do
if i ~= sy then
table.remove(lines, i)
table.remove(bgLines, i)
table.remove(fgLines, i)
end
end
textX, textY = sx, sy
startSelX, endSelX, startSelY, endSelY = nil, nil, nil, nil
return self
end
local function stringGetPositions(str, word)
local pos = {}
if(str:len()>0)then
for w in gmatch(str, word)do
local s, e = find(str, w)
if(s~=nil)and(e~=nil)then
table.insert(pos,s)
table.insert(pos,e)
local startL = sub(str, 1, (s-1))
local endL = sub(str, e+1, str:len())
str = startL..(":"):rep(w:len())..endL
end
end
end
return pos
end
local function updateColors(self, l)
l = l or textY
local fgLine = tHex[self:getForeground()]:rep(fgLines[l]:len())
local bgLine = tHex[self:getBackground()]:rep(bgLines[l]:len())
for k,v in pairs(rules)do
local pos = stringGetPositions(lines[l], v[1])
if(#pos>0)then
for x=1,#pos/2 do
local xP = x*2 - 1
if(v[2]~=nil)then
fgLine = fgLine:sub(1, pos[xP]-1)..tHex[v[2]]:rep(pos[xP+1]-(pos[xP]-1))..fgLine:sub(pos[xP+1]+1, fgLine:len())
end
if(v[3]~=nil)then
bgLine = bgLine:sub(1, pos[xP]-1)..tHex[v[3]]:rep(pos[xP+1]-(pos[xP]-1))..bgLine:sub(pos[xP+1]+1, bgLine:len())
end
end
end
end
for k,v in pairs(keyWords)do
for _,b in pairs(v)do
local pos = stringGetPositions(lines[l], b)
if(#pos>0)then
for x=1,#pos/2 do
local xP = x*2 - 1
fgLine = fgLine:sub(1, pos[xP]-1)..tHex[k]:rep(pos[xP+1]-(pos[xP]-1))..fgLine:sub(pos[xP+1]+1, fgLine:len())
end
end
end
end
fgLines[l] = fgLine
bgLines[l] = bgLine
self:updateDraw()
end
local function updateAllColors(self)
for n=1,#lines do
updateColors(self, n)
end
end
local object = {
getType = function(self)
return objectType
end;
setBackground = function(self, bg)
base.setBackground(self, bg)
updateAllColors(self)
return self
end,
setForeground = function(self, fg)
base.setForeground(self, fg)
updateAllColors(self)
return self
end,
setSelection = function(self, fg, bg)
selectionFG = fg or selectionFG
selectionBG = bg or selectionBG
return self
end,
setSelectionFG = function(self, fg)
return self:setSelection(fg, nil)
end,
setSelectionBG = function(self, bg)
return self:setSelection(nil, bg)
end,
getSelection = function(self)
return selectionFG, selectionBG
end,
getSelectionFG = function(self)
return selectionFG
end,
getSelectionBG = function(self)
return selectionBG
end,
getLines = function(self)
return lines
end,
getLine = function(self, index)
return lines[index]
end,
editLine = function(self, index, text)
lines[index] = text or lines[index]
updateColors(self, index)
self:updateDraw()
return self
end,
clear = function(self)
lines = {""}
bgLines = {""}
fgLines = {""}
startSelX,endSelX,startSelY,endSelY = nil,nil,nil,nil
hIndex, wIndex, textX, textY = 1, 1, 1, 1
self:updateDraw()
return self
end,
addLine = function(self, text, index)
if(text~=nil)then
local bgColor = self:getBackground()
local fgColor = self:getForeground()
if(#lines==1)and(lines[1]=="")then
lines[1] = text
bgLines[1] = tHex[bgColor]:rep(text:len())
fgLines[1] = tHex[fgColor]:rep(text:len())
updateColors(self, 1)
return self
end
if (index ~= nil) then
table.insert(lines, index, text)
table.insert(bgLines, index, tHex[bgColor]:rep(text:len()))
table.insert(fgLines, index, tHex[fgColor]:rep(text:len()))
else
table.insert(lines, text)
table.insert(bgLines, tHex[bgColor]:rep(text:len()))
table.insert(fgLines, tHex[fgColor]:rep(text:len()))
end
end
updateColors(self, index or #lines)
self:updateDraw()
return self
end;
addKeywords = function(self, color, tab)
if(keyWords[color]==nil)then
keyWords[color] = {}
end
for k,v in pairs(tab)do
table.insert(keyWords[color], v)
end
self:updateDraw()
return self
end;
addRule = function(self, rule, fg, bg)
table.insert(rules, {rule, fg, bg})
self:updateDraw()
return self
end;
editRule = function(self, rule, fg, bg)
for k,v in pairs(rules)do
if(v[1]==rule)then
rules[k][2] = fg
rules[k][3] = bg
end
end
self:updateDraw()
return self
end;
removeRule = function(self, rule)
for k,v in pairs(rules)do
if(v[1]==rule)then
table.remove(rules, k)
end
end
self:updateDraw()
return self
end,
setKeywords = function(self, color, tab)
keyWords[color] = tab
self:updateDraw()
return self
end,
removeLine = function(self, index)
if(#lines>1)then
table.remove(lines, index or #lines)
table.remove(bgLines, index or #bgLines)
table.remove(fgLines, index or #fgLines)
else
lines = {""}
bgLines = {""}
fgLines = {""}
end
self:updateDraw()
return self
end,
getTextCursor = function(self)
return textX, textY
end,
getOffset = function(self)
return wIndex, hIndex
end,
setOffset = function(self, xOff, yOff)
wIndex = xOff or wIndex
hIndex = yOff or hIndex
self:updateDraw()
return self
end,
getXOffset = function(self)
return wIndex
end,
setXOffset = function(self, xOff)
return self:setOffset(xOff, nil)
end,
getYOffset = function(self)
return hIndex
end,
setYOffset = function(self, yOff)
return self:setOffset(nil, yOff)
end,
getFocusHandler = function(self)
base.getFocusHandler(self)
local obx, oby = self:getPosition()
self:getParent():setCursor(true, obx + textX - wIndex, oby + textY - hIndex, self:getForeground())
end,
loseFocusHandler = function(self)
base.loseFocusHandler(self)
self:getParent():setCursor(false)
end,
keyHandler = function(self, key)
if (base.keyHandler(self, key)) then
local parent = self:getParent()
local obx, oby = self:getPosition()
local w,h = self:getSize()
if (key == keys.backspace) then
-- on backspace
if(isSelected())then
removeSelection(self)
else
if (lines[textY] == "") then
if (textY > 1) then
table.remove(lines, textY)
table.remove(fgLines, textY)
table.remove(bgLines, textY)
textX = lines[textY - 1]:len() + 1
wIndex = textX - w + 1
if (wIndex < 1) then
wIndex = 1
end
textY = textY - 1
end
elseif (textX <= 1) then
if (textY > 1) then
textX = lines[textY - 1]:len() + 1
wIndex = textX - w + 1
if (wIndex < 1) then
wIndex = 1
end
lines[textY - 1] = lines[textY - 1] .. lines[textY]
fgLines[textY - 1] = fgLines[textY - 1] .. fgLines[textY]
bgLines[textY - 1] = bgLines[textY - 1] .. bgLines[textY]
table.remove(lines, textY)
table.remove(fgLines, textY)
table.remove(bgLines, textY)
textY = textY - 1
end
else
lines[textY] = lines[textY]:sub(1, textX - 2) .. lines[textY]:sub(textX, lines[textY]:len())
fgLines[textY] = fgLines[textY]:sub(1, textX - 2) .. fgLines[textY]:sub(textX, fgLines[textY]:len())
bgLines[textY] = bgLines[textY]:sub(1, textX - 2) .. bgLines[textY]:sub(textX, bgLines[textY]:len())
if (textX > 1) then
textX = textX - 1
end
if (wIndex > 1) then
if (textX < wIndex) then
wIndex = wIndex - 1
end
end
end
if (textY < hIndex) then
hIndex = hIndex - 1
end
end
updateColors(self)
self:setValue("")
elseif (key == keys.delete) then
-- on delete
if(isSelected())then
removeSelection(self)
else
if (textX > lines[textY]:len()) then
if (lines[textY + 1] ~= nil) then
lines[textY] = lines[textY] .. lines[textY + 1]
table.remove(lines, textY + 1)
table.remove(bgLines, textY + 1)
table.remove(fgLines, textY + 1)
end
else
lines[textY] = lines[textY]:sub(1, textX - 1) .. lines[textY]:sub(textX + 1, lines[textY]:len())
fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. fgLines[textY]:sub(textX + 1, fgLines[textY]:len())
bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. bgLines[textY]:sub(textX + 1, bgLines[textY]:len())
end
end
updateColors(self)
elseif (key == keys.enter) then
if(isSelected())then
removeSelection(self)
end
-- on enter
table.insert(lines, textY + 1, lines[textY]:sub(textX, lines[textY]:len()))
table.insert(fgLines, textY + 1, fgLines[textY]:sub(textX, fgLines[textY]:len()))
table.insert(bgLines, textY + 1, bgLines[textY]:sub(textX, bgLines[textY]:len()))
lines[textY] = lines[textY]:sub(1, textX - 1)
fgLines[textY] = fgLines[textY]:sub(1, textX - 1)
bgLines[textY] = bgLines[textY]:sub(1, textX - 1)
textY = textY + 1
textX = 1
wIndex = 1
if (textY - hIndex >= h) then
hIndex = hIndex + 1
end
self:setValue("")
elseif (key == keys.up) then
startSelX, startSelY, endSelX, endSelY = nil, nil, nil, nil
-- arrow up
if (textY > 1) then
textY = textY - 1
if (textX > lines[textY]:len() + 1) then
textX = lines[textY]:len() + 1
end
if (wIndex > 1) then
if (textX < wIndex) then
wIndex = textX - w + 1
if (wIndex < 1) then
wIndex = 1
end
end
end
if (hIndex > 1) then
if (textY < hIndex) then
hIndex = hIndex - 1
end
end
end
elseif (key == keys.down) then
startSelX, startSelY, endSelX, endSelY = nil, nil, nil, nil
-- arrow down
if (textY < #lines) then
textY = textY + 1
if (textX > lines[textY]:len() + 1) then
textX = lines[textY]:len() + 1
end
if (wIndex > 1) then
if (textX < wIndex) then
wIndex = textX - w + 1
if (wIndex < 1) then
wIndex = 1
end
end
end
if (textY >= hIndex + h) then
hIndex = hIndex + 1
end
end
elseif (key == keys.right) then
startSelX, startSelY, endSelX, endSelY = nil, nil, nil, nil
-- arrow right
textX = textX + 1
if (textY < #lines) then
if (textX > lines[textY]:len() + 1) then
textX = 1
textY = textY + 1
end
elseif (textX > lines[textY]:len()) then
textX = lines[textY]:len() + 1
end
if (textX < 1) then
textX = 1
end
if (textX < wIndex) or (textX >= w + wIndex) then
wIndex = textX - w + 1
end
if (wIndex < 1) then
wIndex = 1
end
elseif (key == keys.left) then
startSelX, startSelY, endSelX, endSelY = nil, nil, nil, nil
-- arrow left
textX = textX - 1
if (textX >= 1) then
if (textX < wIndex) or (textX >= w + wIndex) then
wIndex = textX
end
end
if (textY > 1) then
if (textX < 1) then
textY = textY - 1
textX = lines[textY]:len() + 1
wIndex = textX - w + 1
end
end
if (textX < 1) then
textX = 1
end
if (wIndex < 1) then
wIndex = 1
end
elseif(key == keys.tab)then
if(textX % 3 == 0 )then
lines[textY] = lines[textY]:sub(1, textX - 1) .. " " .. lines[textY]:sub(textX, lines[textY]:len())
fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. tHex[self:getForeground()] .. fgLines[textY]:sub(textX, fgLines[textY]:len())
bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. tHex[self:getBackground()] .. bgLines[textY]:sub(textX, bgLines[textY]:len())
textX = textX + 1
end
while textX % 3 ~= 0 do
lines[textY] = lines[textY]:sub(1, textX - 1) .. " " .. lines[textY]:sub(textX, lines[textY]:len())
fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. tHex[self:getForeground()] .. fgLines[textY]:sub(textX, fgLines[textY]:len())
bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. tHex[self:getBackground()] .. bgLines[textY]:sub(textX, bgLines[textY]:len())
textX = textX + 1
end
end
if not((obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (oby + textY - hIndex >= oby and oby + textY - hIndex < oby + h)) then
wIndex = math.max(1, lines[textY]:len()-w+1)
hIndex = math.max(1, textY - h + 1)
end
local cursorX = (textX <= lines[textY]:len() and textX - 1 or lines[textY]:len()) - (wIndex - 1)
if (cursorX > self:getX() + w - 1) then
cursorX = self:getX() + w - 1
end
local cursorY = (textY - hIndex < h and textY - hIndex or textY - hIndex - 1)
if (cursorX < 1) then
cursorX = 0
end
parent:setCursor(true, obx + cursorX, oby + cursorY, self:getForeground())
self:updateDraw()
return true
end
end,
charHandler = function(self, char)
if(base.charHandler(self, char))then
local parent = self:getParent()
local obx, oby = self:getPosition()
local w,h = self:getSize()
if(isSelected())then
removeSelection(self)
end
lines[textY] = lines[textY]:sub(1, textX - 1) .. char .. lines[textY]:sub(textX, lines[textY]:len())
fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. tHex[self:getForeground()] .. fgLines[textY]:sub(textX, fgLines[textY]:len())
bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. tHex[self:getBackground()] .. bgLines[textY]:sub(textX, bgLines[textY]:len())
textX = textX + 1
if (textX >= w + wIndex) then
wIndex = wIndex + 1
end
updateColors(self)
self:setValue("")
if not((obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (oby + textY - hIndex >= oby and oby + textY - hIndex < oby + h)) then
wIndex = math.max(1, lines[textY]:len()-w+1)
hIndex = math.max(1, textY - h + 1)
end
local cursorX = (textX <= lines[textY]:len() and textX - 1 or lines[textY]:len()) - (wIndex - 1)
if (cursorX > self:getX() + w - 1) then
cursorX = self:getX() + w - 1
end
local cursorY = (textY - hIndex < h and textY - hIndex or textY - hIndex - 1)
if (cursorX < 1) then
cursorX = 0
end
parent:setCursor(true, obx + cursorX, oby + cursorY, self:getForeground())
self:updateDraw()
return true
end
end,
dragHandler = function(self, button, x, y)
if (base.dragHandler(self, button, x, y)) then
local parent = self:getParent()
local obx, oby = self:getAbsolutePosition()
local ox, oy = self:getPosition()
local w,h = self:getSize()
if (lines[y - oby + hIndex] ~= nil) then
if(x - obx + wIndex > 0)and(x - obx + wIndex <= w)then
textX = x - obx + wIndex
textY = y - oby + hIndex
if textX > lines[textY]:len() then
textX = lines[textY]:len() + 1
end
endSelX = textX
endSelY = textY
if textX < wIndex then
wIndex = textX - 1
if wIndex < 1 then
wIndex = 1
end
end
parent:setCursor(not isSelected(), ox + textX - wIndex, oy + textY - hIndex, self:getForeground())
self:updateDraw()
end
end
return true
end
end,
scrollHandler = function(self, dir, x, y)
if (base.scrollHandler(self, dir, x, y)) then
local parent = self:getParent()
local obx, oby = self:getAbsolutePosition()
local anchx, anchy = self:getPosition()
local w,h = self:getSize()
hIndex = hIndex + dir
if (hIndex > #lines - (h - 1)) then
hIndex = #lines - (h - 1)
end
if (hIndex < 1) then
hIndex = 1
end
if (obx + textX - wIndex >= obx and obx + textX - wIndex < obx + w) and (anchy + textY - hIndex >= anchy and anchy + textY - hIndex < anchy + h) then
parent:setCursor(not isSelected(), anchx + textX - wIndex, anchy + textY - hIndex, self:getForeground())
else
parent:setCursor(false)
end
self:updateDraw()
return true
end
end,
mouseHandler = function(self, button, x, y)
if (base.mouseHandler(self, button, x, y)) then
local parent = self:getParent()
local obx, oby = self:getAbsolutePosition()
local anchx, anchy = self:getPosition()
if (lines[y - oby + hIndex] ~= nil) then
textX = x - obx + wIndex
textY = y - oby + hIndex
endSelX = nil
endSelY = nil
startSelX = textX
startSelY = textY
if (textX > lines[textY]:len()) then
textX = lines[textY]:len() + 1
startSelX = textX
end
if (textX < wIndex) then
wIndex = textX - 1
if (wIndex < 1) then
wIndex = 1
end
end
self:updateDraw()
end
parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, self:getForeground())
return true
end
end,
mouseUpHandler = function(self, button, x, y)
if (base.mouseUpHandler(self, button, x, y)) then
local obx, oby = self:getAbsolutePosition()
if (lines[y - oby + hIndex] ~= nil) then
endSelX = x - obx + wIndex
endSelY = y - oby + hIndex
if (endSelX > lines[endSelY]:len()) then
endSelX = lines[endSelY]:len() + 1
end
if(startSelX==endSelX)and(startSelY==endSelY)then
startSelX, endSelX, startSelY, endSelY = nil, nil, nil, nil
end
self:updateDraw()
end
return true
end
end,
eventHandler = function(self, event, paste, ...)
base.eventHandler(self, event, paste, ...)
if(event=="paste")then
if(self:isFocused())then
local parent = self:getParent()
local fgColor, bgColor = self:getForeground(), self:getBackground()
local w, h = self:getSize()
lines[textY] = lines[textY]:sub(1, textX - 1) .. paste .. lines[textY]:sub(textX, lines[textY]:len())
fgLines[textY] = fgLines[textY]:sub(1, textX - 1) .. tHex[fgColor]:rep(paste:len()) .. fgLines[textY]:sub(textX, fgLines[textY]:len())
bgLines[textY] = bgLines[textY]:sub(1, textX - 1) .. tHex[bgColor]:rep(paste:len()) .. bgLines[textY]:sub(textX, bgLines[textY]:len())
textX = textX + paste:len()
if (textX >= w + wIndex) then
wIndex = (textX+1)-w
end
local anchx, anchy = self:getPosition()
parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex, fgColor)
updateColors(self)
self:updateDraw()
end
end
end,
draw = function(self)
base.draw(self)
self:addDraw("textfield", function()
local w, h = self:getSize()
local bgColor = tHex[self:getBackground()]
local fgColor = tHex[self:getForeground()]
for n = 1, h do
local text = ""
local bg = ""
local fg = ""
if lines[n + hIndex - 1] then
text = lines[n + hIndex - 1]
fg = fgLines[n + hIndex - 1]
bg = bgLines[n + hIndex - 1]
end
text = sub(text, wIndex, w + wIndex - 1)
bg = rep(bgColor, w)
fg = rep(fgColor, w)
self:addText(1, n, text)
self:addBG(1, n, bg)
self:addFG(1, n, fg)
self:addBlit(1, n, text, fg, bg)
end
if startSelX and endSelX and startSelY and endSelY then
local sx, ex, sy, ey = getSelectionCoordinates()
for n = sy, ey do
local line = #lines[n]
local xOffset = 0
if n == sy and n == ey then
xOffset = sx - 1 - (wIndex - 1)
line = line - (sx - 1 - (wIndex - 1)) - (line - ex + (wIndex - 1))
elseif n == ey then
line = line - (line - ex + (wIndex - 1))
elseif n == sy then
line = line - (sx - 1)
xOffset = sx - 1 - (wIndex - 1)
end
local visible_line_length = math.min(line, w - xOffset)
self:addBG(1 + xOffset, n, rep(tHex[selectionBG], visible_line_length))
self:addFG(1 + xOffset, n, rep(tHex[selectionFG], visible_line_length))
end
end
end)
end,
load = function(self)
self:listenEvent("mouse_click")
self:listenEvent("mouse_up")
self:listenEvent("mouse_scroll")
self:listenEvent("mouse_drag")
self:listenEvent("key")
self:listenEvent("char")
self:listenEvent("other_event")
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,112 @@
return function(name, basalt)
local base = basalt.getObject("Object")(name, basalt)
local objectType = "Thread"
local func
local cRoutine
local isActive = false
local filter
local object = {
getType = function(self)
return objectType
end,
start = function(self, f)
if (f == nil) then
error("Function provided to thread is nil")
end
func = f
cRoutine = coroutine.create(func)
isActive = true
filter=nil
local ok, result = coroutine.resume(cRoutine)
filter = result
if not (ok) then
if (result ~= "Terminated") then
error("Thread Error Occurred - " .. result)
end
end
self:listenEvent("mouse_click")
self:listenEvent("mouse_up")
self:listenEvent("mouse_scroll")
self:listenEvent("mouse_drag")
self:listenEvent("key")
self:listenEvent("key_up")
self:listenEvent("char")
self:listenEvent("other_event")
return self
end,
getStatus = function(self, f)
if (cRoutine ~= nil) then
return coroutine.status(cRoutine)
end
return nil
end,
stop = function(self, f)
isActive = false
self:listenEvent("mouse_click", false)
self:listenEvent("mouse_up", false)
self:listenEvent("mouse_scroll", false)
self:listenEvent("mouse_drag", false)
self:listenEvent("key", false)
self:listenEvent("key_up", false)
self:listenEvent("char", false)
self:listenEvent("other_event", false)
return self
end,
mouseHandler = function(self, ...)
self:eventHandler("mouse_click", ...)
end,
mouseUpHandler = function(self, ...)
self:eventHandler("mouse_up", ...)
end,
mouseScrollHandler = function(self, ...)
self:eventHandler("mouse_scroll", ...)
end,
mouseDragHandler = function(self, ...)
self:eventHandler("mouse_drag", ...)
end,
mouseMoveHandler = function(self, ...)
self:eventHandler("mouse_move", ...)
end,
keyHandler = function(self, ...)
self:eventHandler("key", ...)
end,
keyUpHandler = function(self, ...)
self:eventHandler("key_up", ...)
end,
charHandler = function(self, ...)
self:eventHandler("char", ...)
end,
eventHandler = function(self, event, ...)
base.eventHandler(self, event, ...)
if (isActive) then
if (coroutine.status(cRoutine) == "suspended") then
if(filter~=nil)then
if(event~=filter)then return end
filter=nil
end
local ok, result = coroutine.resume(cRoutine, event, ...)
filter = result
if not (ok) then
if (result ~= "Terminated") then
error("Thread Error Occurred - " .. result)
end
end
else
self:stop()
end
end
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,81 @@
return function(name, basalt)
local base = basalt.getObject("Object")(name, basalt)
local objectType = "Timer"
local timer = 0
local savedRepeats = 0
local repeats = 0
local timerObj
local timerIsActive = false
local object = {
getType = function(self)
return objectType
end,
setTime = function(self, _timer, _repeats)
timer = _timer or 0
savedRepeats = _repeats or 1
return self
end,
getTime = function(self)
return timer
end,
start = function(self)
if(timerIsActive)then
os.cancelTimer(timerObj)
end
repeats = savedRepeats
timerObj = os.startTimer(timer)
timerIsActive = true
self:listenEvent("other_event")
return self
end,
isActive = function(self)
return timerIsActive
end,
cancel = function(self)
if (timerObj ~= nil) then
os.cancelTimer(timerObj)
end
timerIsActive = false
self:removeEvent("other_event")
return self
end,
setStart = function(self, start)
if (start == true) then
return self:start()
else
return self:cancel()
end
end,
onCall = function(self, func)
self:registerEvent("timed_event", func)
return self
end,
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
repeats = repeats - 1
if (repeats >= 1) then
timerObj = os.startTimer(timer)
end
elseif (repeats == -1) then
timerObj = os.startTimer(timer)
end
end
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,356 @@
local utils = require("utils")
local tHex = require("tHex")
return function(name, basalt)
local base = basalt.getObject("ChangeableObject")(name, basalt)
local objectType = "Treeview"
local nodes = {}
local itemSelectedBG = colors.black
local itemSelectedFG = colors.lightGray
local selectionColorActive = true
local textAlign = "left"
local xOffset, yOffset = 0, 0
local scrollable = true
base:setSize(16, 8)
base:setZIndex(5)
local function newNode(text, expandable)
text = text or ""
expandable = expandable or false
local expanded = false
local parent = nil
local children = {}
local node = {}
local onSelect
node = {
getChildren = function(self)
return children
end,
setParent = function(self, p)
if(parent~=nil)then
parent.removeChild(parent.findChildrenByText(node.getText()))
end
parent = p
base:updateDraw()
return node
end,
getParent = function(self)
return parent
end,
addChild = function(self, text, expandable)
local childNode = newNode(text, expandable)
childNode.setParent(node)
table.insert(children, childNode)
base:updateDraw()
return childNode
end,
setExpanded = function(self, exp)
if(expandable)then
expanded = exp
end
base:updateDraw()
return node
end,
isExpanded = function(self)
return expanded
end,
onSelect = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
onSelect = v
end
end
return node
end,
callOnSelect = function(self)
if(onSelect~=nil)then
onSelect(node)
end
end,
setExpandable = function(self, expandable)
expandable = expandable
base:updateDraw()
return node
end,
isExpandable = function(self)
return expandable
end,
removeChild = function(self, index)
if(type(index)=="table")then
for k,v in pairs(index)do
if(v==index)then
index = k
break
end
end
end
table.remove(children, index)
base:updateDraw()
return node
end,
findChildrenByText = function(self, searchText)
local foundNodes = {}
for _, child in ipairs(children) do
if string.find(child.getText(), searchText) then
table.insert(foundNodes, child)
end
end
return foundNodes
end,
getText = function(self)
return text
end,
setText = function(self, t)
text = t
base:updateDraw()
return node
end
}
return node
end
local root = newNode("Root", true)
root:setExpanded(true)
local object = {
init = function(self)
local parent = self:getParent()
self:listenEvent("mouse_click")
self:listenEvent("mouse_scroll")
return base.init(self)
end,
getBase = function(self)
return base
end,
getType = function(self)
return objectType
end,
isType = function(self, t)
return objectType == t or base.isType ~= nil and base.isType(t) or false
end,
setOffset = function(self, x, y)
xOffset = x
yOffset = y
return self
end,
setXOffset = function(self, x)
return self:setOffset(x, yOffset)
end,
setYOffset = function(self, y)
return self:setOffset(xOffset, y)
end,
getOffset = function(self)
return xOffset, yOffset
end,
getXOffset = function(self)
return xOffset
end,
getYOffset = function(self)
return yOffset
end,
setScrollable = function(self, scroll)
scrollable = scroll
return self
end,
getScrollable = function(self, scroll)
return scrollable
end,
setSelectionColor = function(self, bgCol, fgCol, active)
itemSelectedBG = bgCol or self:getBackground()
itemSelectedFG = fgCol or self:getForeground()
selectionColorActive = active~=nil and active or true
self:updateDraw()
return self
end,
setSelectionBG = function(self, bgCol)
return self:setSelectionColor(bgCol, nil, selectionColorActive)
end,
setSelectionFG = function(self, fgCol)
return self:setSelectionColor(nil, fgCol, selectionColorActive)
end,
getSelectionColor = function(self)
return itemSelectedBG, itemSelectedFG
end,
getSelectionBG = function(self)
return itemSelectedBG
end,
getSelectionFG = function(self)
return itemSelectedFG
end,
isSelectionColorActive = function(self)
return selectionColorActive
end,
getRoot = function(self)
return root
end,
setRoot = function(self, node)
root = node
node.setParent(nil)
return self
end,
onSelect = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("treeview_select", v)
end
end
return self
end,
selectionHandler = function(self, node)
node.callOnSelect(node)
self:sendEvent("treeview_select", node)
return self
end,
mouseHandler = function(self, button, x, y)
if base.mouseHandler(self, button, x, y) then
local currentLine = 1 - yOffset
local obx, oby = self:getAbsolutePosition()
local w, h = self:getSize()
local function checkNodeClick(node, level)
if y == oby+currentLine-1 then
if x >= obx and x < obx + w then
node:setExpanded(not node:isExpanded())
self:selectionHandler(node)
self:setValue(node)
self:updateDraw()
return true
end
end
currentLine = currentLine + 1
if node:isExpanded() then
for _, child in ipairs(node:getChildren()) do
if checkNodeClick(child, level + 1) then
return true
end
end
end
return false
end
for _, item in ipairs(root:getChildren()) do
if checkNodeClick(item, 1) then
return true
end
end
end
end,
scrollHandler = function(self, dir, x, y)
if base.scrollHandler(self, dir, x, y) then
if scrollable then
local _, h = self:getSize()
yOffset = yOffset + dir
if yOffset < 0 then
yOffset = 0
end
if dir >= 1 then
local visibleLines = 0
local function countVisibleLines(node, level)
visibleLines = visibleLines + 1
if node:isExpanded() then
for _, child in ipairs(node:getChildren()) do
countVisibleLines(child, level + 1)
end
end
end
for _, item in ipairs(root:getChildren()) do
countVisibleLines(item, 1)
end
if visibleLines > h then
if yOffset > visibleLines - h then
yOffset = visibleLines - h
end
else
yOffset = yOffset - 1
end
end
self:updateDraw()
end
return true
end
return false
end,
draw = function(self)
base.draw(self)
self:addDraw("treeview", function()
local currentLine = 1 - yOffset
local lastClickedNode = self:getValue()
local function drawNode(node, level)
local w, h = self:getSize()
if currentLine >= 1 and currentLine <= h then
local bg = (node == lastClickedNode) and itemSelectedBG or self:getBackground()
local fg = (node == lastClickedNode) and itemSelectedFG or self:getForeground()
local text = node.getText()
self:addBlit(1 + level + xOffset, currentLine, text, tHex[fg]:rep(#text), tHex[bg]:rep(#text))
end
currentLine = currentLine + 1
if node:isExpanded() then
for _, child in ipairs(node:getChildren()) do
drawNode(child, level + 1)
end
end
end
for _, item in ipairs(root:getChildren()) do
drawNode(item, 1)
end
end)
end,
}
object.__index = object
return setmetatable(object, base)
end

View File

@@ -0,0 +1,646 @@
local utils = require("utils")
local tHex = require("tHex")
local sub, find, insert = string.sub, string.find, table.insert
return function(name, basalt)
local base = basalt.getObject("Object")(name, basalt)
-- Base object
local objectType = "VisualObject" -- not changeable
local isVisible,ignOffset,isHovered,isClicked,isDragging = true,false,false,false,false
local zIndex = 1
local x, y, width, height = 1,1,1,1
local dragStartX, dragStartY, dragXOffset, dragYOffset = 0, 0, 0, 0
local bgColor,fgColor, transparency = colors.black, colors.white, false
local parent
local preDrawQueue = {}
local drawQueue = {}
local postDrawQueue = {}
local renderObject = {}
local function split(str, d)
local result = {}
if str == "" then
return result
end
d = d or " "
local start = 1
local delim_start, delim_end = find(str, d, start)
while delim_start do
insert(result, {x=start, value=sub(str, start, delim_start - 1)})
start = delim_end + 1
delim_start, delim_end = find(str, d, start)
end
insert(result, {x=start, value=sub(str, start)})
return result
end
local object = {
getType = function(self)
return objectType
end,
getBase = function(self)
return base
end,
isType = function(self, t)
return objectType==t or base.isType~=nil and base.isType(t) or false
end,
getBasalt = function(self)
return basalt
end,
show = function(self)
isVisible = true
self:updateDraw()
return self
end,
hide = function(self)
isVisible = false
self:updateDraw()
return self
end,
isVisible = function(self)
return isVisible
end,
setVisible = function(self, _isVisible)
isVisible = _isVisible or not isVisible
self:updateDraw()
return self
end,
setTransparency = function(self, _transparency)
transparency = _transparency~= nil and _transparency or true
self:updateDraw()
return self
end,
setParent = function(self, newParent, noRemove)
base.setParent(self, newParent, noRemove)
parent = newParent
return self
end,
setFocus = function(self)
if (parent ~= nil) then
parent:setFocusedChild(self)
end
return self
end,
setZIndex = function(self, index)
zIndex = index
if (parent ~= nil) then
parent:updateZIndex(self, zIndex)
self:updateDraw()
end
return self
end,
getZIndex = function(self)
return zIndex
end,
updateDraw = function(self)
if (parent ~= nil) then
parent:updateDraw()
end
return self
end,
setPosition = function(self, xPos, yPos, rel)
local curX, curY = x, y
if(type(xPos)=="number")then
x = rel and x+xPos or xPos
end
if(type(yPos)=="number")then
y = rel and y+yPos or yPos
end
if(parent~=nil)then parent:customEventHandler("basalt_FrameReposition", self) end
if(self:getType()=="Container")then parent:customEventHandler("basalt_FrameReposition", self) end
self:updateDraw()
self:repositionHandler(curX, curY)
return self
end,
getX = function(self)
return x
end,
setX = function(self, newX)
return self:setPosition(newX, y)
end,
getY = function(self)
return y
end,
setY = function(self, newY)
return self:setPosition(x, newY)
end,
getPosition = function(self)
return x, y
end,
setSize = function(self, newWidth, newHeight, rel)
local oldW, oldH = width, height
if(type(newWidth)=="number")then
width = rel and width+newWidth or newWidth
end
if(type(newHeight)=="number")then
height = rel and height+newHeight or newHeight
end
if(parent~=nil)then
parent:customEventHandler("basalt_FrameResize", self)
if(self:getType()=="Container")then parent:customEventHandler("basalt_FrameResize", self) end
end
self:resizeHandler(oldW, oldH)
self:updateDraw()
return self
end,
getHeight = function(self)
return height
end,
setHeight = function(self, newHeight)
return self:setSize(width, newHeight)
end,
getWidth = function(self)
return width
end,
setWidth = function(self, newWidth)
return self:setSize(newWidth, height)
end,
getSize = function(self)
return width, height
end,
setBackground = function(self, color)
bgColor = color
self:updateDraw()
return self
end,
getBackground = function(self)
return bgColor
end,
setForeground = function(self, color)
fgColor = color or false
self:updateDraw()
return self
end,
getForeground = function(self)
return fgColor
end,
getAbsolutePosition = function(self, x, y)
-- relative position to absolute position
if (x == nil) or (y == nil) then
x, y = self:getPosition()
end
if (parent ~= nil) then
local fx, fy = parent:getAbsolutePosition()
x = fx + x - 1
y = fy + y - 1
end
return x, y
end,
ignoreOffset = function(self, ignore)
ignOffset = ignore
if(ignore==nil)then ignOffset = true end
return self
end,
getIgnoreOffset = function(self)
return ignOffset
end,
isCoordsInObject = function(self, x, y)
if(isVisible)and(self:isEnabled())then
if(x==nil)or(y==nil)then return false end
local objX, objY = self:getAbsolutePosition()
local w, h = self:getSize()
if (objX <= x) and (objX + w > x) and (objY <= y) and (objY + h > y) then
return true
end
end
return false
end,
onGetFocus = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("get_focus", v)
end
end
return self
end,
onLoseFocus = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("lose_focus", v)
end
end
return self
end,
isFocused = function(self)
if (parent ~= nil) then
return parent:getFocused() == self
end
return true
end,
resizeHandler = function(self, ...)
if(self:isEnabled())then
local val = self:sendEvent("basalt_resize", ...)
if(val==false)then return false end
end
return true
end,
repositionHandler = function(self, ...)
if(self:isEnabled())then
local val = self:sendEvent("basalt_reposition", ...)
if(val==false)then return false end
end
return true
end,
onResize = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("basalt_resize", v)
end
end
return self
end,
onReposition = function(self, ...)
for _,v in pairs(table.pack(...))do
if(type(v)=="function")then
self:registerEvent("basalt_reposition", v)
end
end
return self
end,
mouseHandler = function(self, button, x, y, isMon)
if(self:isCoordsInObject(x, y))then
local objX, objY = self:getAbsolutePosition()
local val = self:sendEvent("mouse_click", button, x - (objX-1), y - (objY-1), x, y, isMon)
if(val==false)then return false end
if(parent~=nil)then
parent:setFocusedChild(self)
end
isClicked = true
isDragging = true
dragStartX, dragStartY = x, y
return true
end
end,
mouseUpHandler = function(self, button, x, y)
isDragging = false
if(isClicked)then
local objX, objY = self:getAbsolutePosition()
local val = self:sendEvent("mouse_release", button, x - (objX-1), y - (objY-1), x, y)
isClicked = false
end
if(self:isCoordsInObject(x, y))then
local objX, objY = self:getAbsolutePosition()
local val = self:sendEvent("mouse_up", button, x - (objX-1), y - (objY-1), x, y)
if(val==false)then return false end
return true
end
end,
dragHandler = function(self, button, x, y)
if(isDragging)then
local objX, objY = self:getAbsolutePosition()
local val = self:sendEvent("mouse_drag", button, x - (objX-1), y - (objY-1), dragStartX-x, dragStartY-y, x, y)
dragStartX, dragStartY = x, y
if(val~=nil)then return val end
if(parent~=nil)then
parent:setFocusedChild(self)
end
return true
end
if(self:isCoordsInObject(x, y))then
local objX, objY = self:getAbsolutePosition()
dragStartX, dragStartY = x, y
dragXOffset, dragYOffset = objX - x, objY - y
end
end,
scrollHandler = function(self, dir, x, y)
if(self:isCoordsInObject(x, y))then
local objX, objY = self:getAbsolutePosition()
local val = self:sendEvent("mouse_scroll", dir, x - (objX-1), y - (objY-1))
if(val==false)then return false end
if(parent~=nil)then
parent:setFocusedChild(self)
end
return true
end
end,
hoverHandler = function(self, x, y, stopped)
if(self:isCoordsInObject(x, y))then
local val = self:sendEvent("mouse_hover", x, y, stopped)
if(val==false)then return false end
isHovered = true
return true
end
if(isHovered)then
local val = self:sendEvent("mouse_leave", x, y, stopped)
if(val==false)then return false end
isHovered = false
end
end,
keyHandler = function(self, key, isHolding)
if(self:isEnabled())and(isVisible)then
if (self:isFocused()) then
local val = self:sendEvent("key", key, isHolding)
if(val==false)then return false end
return true
end
end
end,
keyUpHandler = function(self, key)
if(self:isEnabled())and(isVisible)then
if (self:isFocused()) then
local val = self:sendEvent("key_up", key)
if(val==false)then return false end
return true
end
end
end,
charHandler = function(self, char)
if(self:isEnabled())and(isVisible)then
if(self:isFocused())then
local val = self:sendEvent("char", char)
if(val==false)then return false end
return true
end
end
end,
getFocusHandler = function(self)
local val = self:sendEvent("get_focus")
if(val~=nil)then return val end
return true
end,
loseFocusHandler = function(self)
isDragging = false
local val = self:sendEvent("lose_focus")
if(val~=nil)then return val end
return true
end,
addDraw = function(self, name, f, pos, typ, active)
local queue = (typ==nil or typ==1) and drawQueue or typ==2 and preDrawQueue or typ==3 and postDrawQueue
pos = pos or #queue+1
if(name~=nil)then
for k,v in pairs(queue)do
if(v.name==name)then
table.remove(queue, k)
break
end
end
local t = {name=name, f=f, pos=pos, active=active~=nil and active or true}
table.insert(queue, pos, t)
end
self:updateDraw()
return self
end,
addPreDraw = function(self, name, f, pos, typ)
self:addDraw(name, f, pos, 2)
return self
end,
addPostDraw = function(self, name, f, pos, typ)
self:addDraw(name, f, pos, 3)
return self
end,
setDrawState = function(self, name, state, typ)
local queue = (typ==nil or typ==1) and drawQueue or typ==2 and preDrawQueue or typ==3 and postDrawQueue
for k,v in pairs(queue)do
if(v.name==name)then
v.active = state
break
end
end
self:updateDraw()
return self
end,
getDrawId = function(self, name, typ)
local queue = typ==1 and drawQueue or typ==2 and preDrawQueue or typ==3 and postDrawQueue or drawQueue
for k,v in pairs(queue)do
if(v.name==name)then
return k
end
end
end,
addText = function(self, x, y, text)
local obj = self:getParent() or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
if not(transparency)then
obj:setText(x+xPos-1, y+yPos-1, text)
return
end
local t = split(text, "\0")
for k,v in pairs(t)do
if(v.value~="")and(v.value~="\0")then
obj:setText(x+v.x+xPos-2, y+yPos-1, v.value)
end
end
end,
addBG = function(self, x, y, bg, noText)
local obj = parent or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
if not(transparency)then
obj:setBG(x+xPos-1, y+yPos-1, bg)
return
end
local t = split(bg)
for k,v in pairs(t)do
if(v.value~="")and(v.value~=" ")then
if(noText~=true)then
obj:setText(x+v.x+xPos-2, y+yPos-1, (" "):rep(#v.value))
obj:setBG(x+v.x+xPos-2, y+yPos-1, v.value)
else
table.insert(renderObject, {x=x+v.x-1,y=y,bg=v.value})
obj:setBG(x+xPos-1, y+yPos-1, fg)
end
end
end
end,
addFG = function(self, x, y, fg)
local obj = parent or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
if not(transparency)then
obj:setFG(x+xPos-1, y+yPos-1, fg)
return
end
local t = split(fg)
for k,v in pairs(t)do
if(v.value~="")and(v.value~=" ")then
obj:setFG(x+v.x+xPos-2, y+yPos-1, v.value)
end
end
end,
addBlit = function(self, x, y, t, fg, bg)
local obj = parent or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
if not(transparency)then
obj:blit(x+xPos-1, y+yPos-1, t, fg, bg)
return
end
local _text = split(t, "\0")
local _fg = split(fg)
local _bg = split(bg)
for k,v in pairs(_text)do
if(v.value~="")or(v.value~="\0")then
obj:setText(x+v.x+xPos-2, y+yPos-1, v.value)
end
end
for k,v in pairs(_bg)do
if(v.value~="")or(v.value~=" ")then
obj:setBG(x+v.x+xPos-2, y+yPos-1, v.value)
end
end
for k,v in pairs(_fg)do
if(v.value~="")or(v.value~=" ")then
obj:setFG(x+v.x+xPos-2, y+yPos-1, v.value)
end
end
end,
addTextBox = function(self, x, y, w, h, text)
local obj = parent or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
obj:drawTextBox(x+xPos-1, y+yPos-1, w, h, text)
end,
addForegroundBox = function(self, x, y, w, h, col)
local obj = parent or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
obj:drawForegroundBox(x+xPos-1, y+yPos-1, w, h, col)
end,
addBackgroundBox = function(self, x, y, w, h, col)
local obj = parent or self
local xPos,yPos = self:getPosition()
if(parent~=nil)then
local xO, yO = parent:getOffset()
xPos = ignOffset and xPos or xPos - xO
yPos = ignOffset and yPos or yPos - yO
end
obj:drawBackgroundBox(x+xPos-1, y+yPos-1, w, h, col)
end,
render = function(self)
if (isVisible)then
self:redraw()
end
end,
redraw = function(self)
for k,v in pairs(preDrawQueue)do
if (v.active)then
v.f(self)
end
end
for k,v in pairs(drawQueue)do
if (v.active)then
v.f(self)
end
end
for k,v in pairs(postDrawQueue)do
if (v.active)then
v.f(self)
end
end
return true
end,
draw = function(self)
self:addDraw("base", function()
local w,h = self:getSize()
if(bgColor~=false)then
self:addTextBox(1, 1, w, h, " ")
self:addBackgroundBox(1, 1, w, h, bgColor)
end
if(fgColor~=false)then
self:addForegroundBox(1, 1, w, h, fgColor)
end
end, 1)
end,
}
object.__index = object
return setmetatable(object, base)
end