diff --git a/Basalt/Frame.lua b/Basalt/Frame.lua index 0bb3c0a..68278d5 100644 --- a/Basalt/Frame.lua +++ b/Basalt/Frame.lua @@ -1,1297 +1,457 @@ -local module = require("module") -local Object = require("Object") -local _OBJECTS = require("loadObjects") -local BasaltDraw = require("basaltDraw") +local basaltEvent = require("basaltEvent")() +local Frame = require("Frame") +local theme = require("theme") local utils = require("utils") -local layout = module("layout") -local basaltMon = module("basaltMon") +local log = require("basaltLogs") local uuid = utils.uuid -local rpairs = utils.rpairs -local xmlValue = utils.getValueFromXML -local tableCount = utils.tableCount +local createText = utils.createText +local count = utils.tableCount +local moveThrottle = 300 +local dragThrottle = 50 -local sub,min,max = string.sub,math.min,math.max +local baseTerm = term.current() +local version = "1.6.4" -return function(name, parent, pTerm, basalt) - -- Frame - local base = Object(name) - local objectType = "Frame" - local objects = {} - local objZIndex = {} - local object = {} - local events = {} - local eventZIndex = {} - local variables = {} - local theme = {} - local dynamicValues = {} - local dynValueId = 0 - local termObject = pTerm or term.current() +local projectDirectory = fs.getDir(table.pack(...)[2] or "") - local monSide = "" - local isMonitor = false - local isGroupedMonitor = false - local monitorAttached = false - local dragXOffset = 0 - local dragYOffset = 0 - local isScrollable = false - local scrollAmount = 0 - local mirrorActive = false - local mirrorAttached = false - local mirrorSide = "" - local isMovable = false - local isDragging =false +local activeKey, frames, monFrames, monGroups, variables, schedules = {}, {}, {}, {}, {}, {} +local mainFrame, activeFrame, focusedObject, updaterActive - local focusedObjectCache - local focusedObject - local autoSize = true - local autoScroll = true - local initialized = false +local basalt = {} - local activeEvents = {} - - local colorTheme = {} - - base:setZIndex(10) - - local basaltDraw = BasaltDraw(termObject) - - local cursorBlink = false - local xCursor = 1 - local yCursor = 1 - local cursorColor = colors.white - - local xOffset, yOffset = 0, 0 - - local lastXMLReferences = {} - - local function xmlDefaultValues(data, obj) - if(obj~=nil)then - obj:setValuesByXMLData(data) - end - end - - local function addXMLObjectType(tab, f, self) - if(tab~=nil)then - if(tab.properties~=nil)then tab = {tab} end - - for k,v in pairs(tab)do - local obj = f(self, v["@id"] or uuid()) - table.insert(lastXMLReferences, obj) - xmlDefaultValues(v, obj) - end - end - end - - local function getObject(name) - if(type(name)~="string")then name = name.name end - for _, value in pairs(objects) do - for _, b in pairs(value) do - if (b:getName() == name) then - return b - end - end - end - end - local function getDeepObject(name) - local o = getObject(name) - if(o~=nil)then return o end - for _, value in pairs(objects) do - for _, b in pairs(value) do - if (b:getType() == "Frame") then - local oF = b:getDeepObject(name) - if(oF~=nil)then return oF end - end - end - end - end - - local function addObject(obj) - local zIndex = obj:getZIndex() - if (getObject(obj.name) ~= nil) then - return - end - if (objects[zIndex] == nil) then - for x = 1, #objZIndex + 1 do - if (objZIndex[x] ~= nil) then - if (zIndex == objZIndex[x]) then - break - end - if (zIndex > objZIndex[x]) then - table.insert(objZIndex, x, zIndex) - break - end - else - table.insert(objZIndex, zIndex) - end - end - if (#objZIndex <= 0) then - table.insert(objZIndex, zIndex) - end - objects[zIndex] = {} - end - obj.parent = object - if(obj.init~=nil)then - obj:init() - end - table.insert(objects[zIndex], obj) - return obj - end - - local function removeEvents(self, obj) - for a, b in pairs(events) do - for c, d in pairs(b) do - for key, value in pairs(d) do - if (value == obj) then - table.remove(events[a][c], key) - if(self.parent~=nil)then - if(tableCount(events[a])<=0)then - self.parent:removeEvent(a, self) - end - end - end - end - end - end - end - - local function removeObject(self, obj) - for a, b in pairs(objects) do - for key, value in pairs(b) do - if(type(obj)=="string")then - if (value:getName() == obj) then - table.remove(objects[a], key) - removeEvents(object, value) - self:updateDraw() - return true - end - else - if (value == obj) then - table.remove(objects[a], key) - removeEvents(object, value) - self:updateDraw() - return true - end - end - end - end - return false - end - - local function getEvent(self, event, name) - for _, value in pairs(events[event]) do - for _, b in pairs(value) do - if (b:getName() == name) then - return b - end - end - end - end - - local function addEvent(self, event, obj) - local zIndex = obj:getZIndex() - if(events[event]==nil)then events[event] = {} end - if(eventZIndex[event]==nil)then eventZIndex[event] = {} end - if (getEvent(self, event, obj.name) ~= nil) then - return - end - if(self.parent~=nil)then - self.parent:addEvent(event, self) - end - activeEvents[event] = true - if (events[event][zIndex] == nil) then - for x = 1, #eventZIndex[event] + 1 do - if (eventZIndex[event][x] ~= nil) then - if (zIndex == eventZIndex[event][x]) then - break - end - if (zIndex > eventZIndex[event][x]) then - table.insert(eventZIndex[event], x, zIndex) - break - end - else - table.insert(eventZIndex[event], zIndex) - end - end - if (#eventZIndex[event] <= 0) then - table.insert(eventZIndex[event], zIndex) - end - events[event][zIndex] = {} - end - table.insert(events[event][zIndex], obj) - return obj - end - - - local function removeEvent(self, event, obj) - if(events[event]~=nil)then - for a, b in pairs(events[event]) do - for key, value in pairs(b) do - if (value == obj) then - table.remove(events[event][a], key) - if(#events[event][a]<=0)then - events[event][a] = nil - if(self.parent~=nil)then - if(tableCount(events[event])<=0)then - activeEvents[event] = false - self.parent:removeEvent(event, self) - end - end - end - return true; - end - end - end - end - return false - end - local math = math - local function stringToNumber(str) - local ok, result = pcall(load("return " .. str, "", nil, {math=math})) - if not(ok)then error(str.." is not a valid dynamic code") end - return result - end - - local function newDynamicValue(_, obj, str) - for k,v in pairs(dynamicValues)do - if(v[2]==str)and(v[4]==obj)then - return v - end - end - dynValueId = dynValueId + 1 - dynamicValues[dynValueId] = {0, str, {}, obj, dynValueId} - return dynamicValues[dynValueId] - end - - local function dynValueGetObjects(obj, str) - local names = {} - local t = {} - for v in str:gmatch("%a+%.x") do - local name = v:gsub("%.x", "") - if(name~="self")and(name~="parent")then - table.insert(names, name) end - end - for v in str:gmatch("%w+%.y") do - local name = v:gsub("%.y", "") - if(name~="self")and(name~="parent")then table.insert(names, name) end - end - for v in str:gmatch("%a+%.w") do - local name = v:gsub("%.w", "") - if(name~="self")and(name~="parent")then - table.insert(names, name) - - end - end - for v in str:gmatch("%a+%.h") do - local name = v:gsub("%.h", "") - if(name~="self")and(name~="parent")then - table.insert(names, name) end - end - for k,v in pairs(names)do - t[v] = getObject(v) - if(t[v]==nil)then - error("Dynamic Values - unable to find object "..v) - end - end - t["self"] = obj - t["parent"] = obj:getParent() - return t - end - - local function dynValueObjectToNumber(str, objList) - local newStr = str - for v in str:gmatch("%w+%.x") do - newStr = newStr:gsub(v, objList[v:gsub("%.x", "")]:getX()) - end - for v in str:gmatch("%w+%.y") do - newStr = newStr:gsub(v, objList[v:gsub("%.y", "")]:getY()) - end - for v in str:gmatch("%w+%.w") do - newStr = newStr:gsub(v, objList[v:gsub("%.w", "")]:getWidth()) - end - for v in str:gmatch("%w+%.h") do - newStr = newStr:gsub(v, objList[v:gsub("%.h", "")]:getHeight()) - end - return newStr - end - - - local function recalculateDynamicValues(self) - if(#dynamicValues>0)then - for n=1,dynValueId do - if(dynamicValues[n]~=nil)then - local numberStr - if(#dynamicValues[n][3]<=0)then dynamicValues[n][3] = dynValueGetObjects(dynamicValues[n][4], dynamicValues[n][2]) end - numberStr = dynValueObjectToNumber(dynamicValues[n][2], dynamicValues[n][3]) - dynamicValues[n][1] = stringToNumber(numberStr) - if(dynamicValues[n][4]:getType()=="Frame")then - dynamicValues[n][4]:recalculateDynamicValues() - end - end - end - for _, index in pairs(objZIndex) do - if (objects[index] ~= nil) then - for _, value in pairs(objects[index]) do - if(basalt.getDynamicValueEventSetting())then - if (value.eventHandler ~= nil) then - value:eventHandler("basalt_dynamicvalue", self) - end - end - if (value.customEventHandler ~= nil) then - value:customEventHandler("basalt_resize", self) - end - end - end - end - end - end - - local function getDynamicValue(id) - return dynamicValues[id][1] - end - - local function getVerticalScrollAmount(self) - local amount = 0 - for _, value in pairs(objects) do - for _, b in pairs(value) do - if(b.getHeight~=nil)and(b.getY~=nil)then - if(b:getType()=="Dropdown")then - local h, y = b:getHeight(), b:getY() - local wD, hD = b:getDropdownSize() - h = h + hD - 1 - if (h + y - self:getHeight() >= amount) then - amount = max(h + y - self:getHeight(), 0) - end - else - local h, y = b:getHeight(), b:getY() - if (h + y - self:getHeight() >= amount) then - amount = max(h + y - self:getHeight(), 0) - end - end - end - end - end - return amount - end - - local function getHorizontalScrollAmount(self) - local amount = 0 - for _, value in pairs(objects) do - for _, b in pairs(value) do - if(b.getWidth~=nil)and(b.getX~=nil)then - local h, y = b:getWidth(), b:getX() - if (h + y - self:getWidth() >= amount) then - amount = max(h + y - self:getWidth(), 0) - end - end - end - end - return amount - end - - local function calculateMaxScroll(self) - if(autoScroll)then - scrollAmount = getVerticalScrollAmount(self) - end - end - - object = { - barActive = false, - barBackground = colors.gray, - barTextcolor = colors.black, - barText = "New Frame", - barTextAlign = "left", - - addEvent = addEvent, - removeEvent = removeEvent, - removeEvents = removeEvents, - getEvent = getEvent, - - newDynamicValue = newDynamicValue, - recalculateDynamicValues = recalculateDynamicValues, - getDynamicValue = getDynamicValue, - - getType = function(self) - return objectType - end, - - getObjectCount = function(self) - local count = 0 - for _, value in pairs(objects) do - count = count+#value - end - return count - end, - getEventCount = function(self, ev) - local count = 0 - for _, value in pairs(events[ev]) do - count = count+#value - end - return count - end, - - setZIndex = function(self, newIndex) - base.setZIndex(self, newIndex) - for k,v in pairs(activeEvents)do - if(v)then - self.parent:addEvent(k, self) - end - end - return self - end, - - setFocusedObject = function(self, obj) - if(focusedObject~=obj)then - if(focusedObject~=nil)then - if(getObject(focusedObject)~=nil)then - focusedObject:loseFocusHandler() - end - end - if(obj~=nil)then - if(getObject(obj)~=nil)then - obj:getFocusHandler() - end - end - focusedObject = obj - end - return self - end; - - getVariable = function(self, name) - return basalt.getVariable(name) - end, - - setSize = function(self, w, h, rel) - base.setSize(self, w, h, rel) - if(self.parent==nil)then - basaltDraw = BasaltDraw(termObject) - end - for _, index in pairs(objZIndex) do - if (objects[index] ~= nil) then - for _, value in pairs(objects[index]) do - if (value.customEventHandler ~= nil) then - value:customEventHandler("basalt_resize", self) - end - end - end - end - self:recalculateDynamicValues() - autoSize = false - return self - end; - - setTheme = function(self, _theme, col) - if(type(_theme)=="table")then - theme = _theme - elseif(type(_theme)=="string")then - theme[_theme] = col - end - self:updateDraw() - return self - end, - - getTheme = function(self, name) - return theme[name] or (self.parent~=nil and self.parent:getTheme(name) or basalt.getTheme(name)) - end, - - getThemeColor = function(self, col) - return col~=nil and colorTheme[col] or colorTheme - end, - - setThemeColor = function(self, col, ...) - if(self.parent==nil)then - 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 - end - return self - end, - - setPosition = function(self, x, y, rel) - base.setPosition(self, x, y, rel) - self:recalculateDynamicValues() - return self - end; - - getBasaltInstance = function(self) - return basalt - end, - - setOffset = function(self, xO, yO) - xOffset = xO ~= nil and math.floor(xO < 0 and math.abs(xO) or -xO) or xOffset - yOffset = yO ~= nil and math.floor(yO < 0 and math.abs(yO) or -yO) or yOffset - self:updateDraw() - return self - end; - - getOffsetInternal = function(self) - return xOffset, yOffset - end; - - getOffset = function(self) - return xOffset < 0 and math.abs(xOffset) or -xOffset, yOffset < 0 and math.abs(yOffset) or -yOffset - end; - - removeFocusedObject = function(self) - if(focusedObject~=nil)then - if(getObject(focusedObject)~=nil)then - focusedObject:loseFocusHandler() - end - end - focusedObject = nil - return self - end; - - getFocusedObject = function(self) - return focusedObject - end; - - setCursor = function(self, _blink, _xCursor, _yCursor, color) - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - self.parent:setCursor(_blink or false, (_xCursor or 0)+obx-1, (_yCursor or 0)+oby-1, color or cursorColor) - else - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition(self:getX(), self:getY(), true)) - cursorBlink = _blink or false - if (_xCursor ~= nil) then - xCursor = obx + _xCursor - 1 - end - if (_yCursor ~= nil) then - yCursor = oby + _yCursor - 1 - end - cursorColor = color or cursorColor - if (cursorBlink) then - termObject.setTextColor(cursorColor) - termObject.setCursorPos(xCursor, yCursor) - termObject.setCursorBlink(cursorBlink) - else - termObject.setCursorBlink(false) - end - end - return self - end; - - setMovable = function(self, movable) - if(self.parent~=nil)then - isMovable = movable or not isMovable - self.parent:addEvent("mouse_click", self) - activeEvents["mouse_click"] = true - self.parent:addEvent("mouse_up", self) - activeEvents["mouse_up"] = true - self.parent:addEvent("mouse_drag", self) - activeEvents["mouse_drag"] = true - end - return self; - end; - - setScrollable = function(self, scrollable) - isScrollable = (scrollable or scrollable==nil) and true or false - if(self.parent~=nil)then - self.parent:addEvent("mouse_scroll", self) - end - activeEvents["mouse_scroll"] = true - return self - end, - - setScrollAmount = function(self, max) - scrollAmount = max or scrollAmount - autoScroll = false - return self - end, - - - getScrollAmount = function(self) - return autoScroll and calculateMaxScroll(self) or scrollAmount - end, - - getCalculatedVerticalScroll = getVerticalScrollAmount, - getCalculatedHorizontalScroll = getHorizontalScrollAmount, - - show = function(self) - base.show(self) - if(self.parent==nil)then - 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 - if(isMonitor)and not(isGroupedMonitor)then - basalt.setMonitorFrame(monSide, self) - elseif(isGroupedMonitor)then - basalt.setMonitorFrame(self:getName(), self, monSide) - else - basalt.setMainFrame(self) - end - end - return self; - end; - - hide = function (self) - base.hide(self) - if(self.parent==nil)then - if(activeFrame == self)then activeFrame = nil end - if(isMonitor)and not(isGroupedMonitor)then - if(basalt.getMonitorFrame(monSide) == self)then - basalt.setActiveFrame(nil) - end - elseif(isGroupedMonitor)then - if(basalt.getMonitorFrame(self:getName()) == self)then - basalt.setActiveFrame(nil) - end - else - if(basalt.getMainFrame() == self)then - basalt.setMainFrame(nil) - end - end - end - return self - end; - - addLayout = function(self, file) - if(file~=nil)then - if(fs.exists(file))then - local f = fs.open(file, "r") - local data = layout:ParseXmlText(f.readAll()) - f.close() - lastXMLReferences = {} - self:setValuesByXMLData(data) - end - end - return self - end, - - getLastLayout = function(self) - return lastXMLReferences - end, - - addLayoutFromString = function(self, str) - if(str~=nil)then - local data = layout:ParseXmlText(str) - self:setValuesByXMLData(data) - end - return self - end, - - setValuesByXMLData = function(self, data) - base.setValuesByXMLData(self, data) - if(xmlValue("movable", data)~=nil)then if(xmlValue("movable", data))then self:setMovable(true) end end - if(xmlValue("scrollable", data)~=nil)then if(xmlValue("scrollable", data))then self:setScrollable(true) end end - if(xmlValue("monitor", data)~=nil)then self:setMonitor(xmlValue("monitor", data)):show() end - if(xmlValue("mirror", data)~=nil)then self:setMirror(xmlValue("mirror", data)) end - if(xmlValue("bar", data)~=nil)then if(xmlValue("bar", data))then self:showBar(true) else self:showBar(false) end end - if(xmlValue("barText", data)~=nil)then self.barText = xmlValue("barText", data) end - if(xmlValue("barBG", data)~=nil)then self.barBackground = colors[xmlValue("barBG", data)] end - if(xmlValue("barFG", data)~=nil)then self.barTextcolor = colors[xmlValue("barFG", data)] end - if(xmlValue("barAlign", data)~=nil)then self.barTextAlign = xmlValue("barAlign", data) end - if(xmlValue("layout", data)~=nil)then self:addLayout(xmlValue("layout", data)) end - if(xmlValue("xOffset", data)~=nil)then self:setOffset(xmlValue("xOffset", data), yOffset) end - if(xmlValue("yOffset", data)~=nil)then self:setOffset(yOffset, xmlValue("yOffset", data)) end - if(xmlValue("scrollAmount", data)~=nil)then self:setScrollAmount(xmlValue("scrollAmount", data)) end - - local objectList = data:children() - - for k,v in pairs(objectList)do - if(v.___name~="animation")then - local name = v.___name:gsub("^%l", string.upper) - if(_OBJECTS[name]~=nil)then - addXMLObjectType(v, self["add"..name], self) - end - end - end - - addXMLObjectType(data["frame"], self.addFrame, self) - addXMLObjectType(data["animation"], self.addAnimation, self) - return self - end, - - showBar = function(self, showIt) -- deprecated - self.barActive = showIt or not self.barActive - self:updateDraw() - return self - end; - - setBar = function(self, text, bgCol, fgCol) -- deprecated - self.barText = text or "" - self.barBackground = bgCol or self.barBackground - self.barTextcolor = fgCol or self.barTextcolor - self:updateDraw() - return self - end; - - setBarTextAlign = function(self, align) -- deprecated - self.barTextAlign = align or "left" - self:updateDraw() - return self - end; - - setMirror = function(self, side) - if(self.parent~=nil)then error("Frame has to be a base frame in order to attach a mirror.") end - mirrorSide = side - if(mirror~=nil)then - basaltDraw.setMirror(mirror) - end - mirrorActive = true - return self - end, - - removeMirror = function(self) - mirror = nil - mirrorActive = false - basaltDraw.setMirror(nil) - return self - end, - - setMonitorScale = function(self, scale) - if(isMonitor)then - termObject.setTextScale(scale) - end - return self - end, - - setMonitor = function(self, side, scale) - if(side~=nil)and(side~=false)then - if(type(side)=="string")then - if(peripheral.getType(side)=="monitor")then - termObject = peripheral.wrap(side) - monitorAttached = true - end - if(self.parent~=nil)then - self.parent:removeObject(self) - end - isMonitor = true - basalt.setMonitorFrame(side, self) - elseif(type(side)=="table")then - termObject = basaltMon(side) - monitorAttached = true - isMonitor = true - isGroupedMonitor = true - basalt.setMonitorFrame(self:getName(), self, true) - end - else - termObject = parentTerminal - isMonitor = false - isGroupedMonitor = false - if(type(monSide)=="string")then - if(basalt.getMonitorFrame(monSide)==self)then - basalt.setMonitorFrame(monSide, nil) - end - else - if(basalt.getMonitorFrame(self:getName())==self)then - basalt.setMonitorFrame(self:getName(), nil) - end - end - end - if(scale~=nil)then termObject.setTextScale(scale) end - basaltDraw = BasaltDraw(termObject) - self:setSize(termObject.getSize()) - autoSize = true - monSide = side or nil - self:updateDraw() - return self; - end; - - loseFocusHandler = function(self) - base.loseFocusHandler(self) - if(focusedObject~=nil)then focusedObject:loseFocusHandler() focusedObject = nil end - end; - - getFocusHandler = function(self) - base.getFocusHandler(self) - if (self.parent ~= nil) then - if(isMovable)then - self.parent:removeEvents(self) - self.parent:removeObject(self) - self.parent:addObject(self) - for k,v in pairs(activeEvents)do - if(v)then - self.parent:addEvent(k, self) - end - end - self:updateDraw() - end - end - if(focusedObject~=nil)then focusedObject:getFocusHandler() end - end; - - eventHandler = function(self, event, ...) - base.eventHandler(self, event, ...) - if(events["other_event"]~=nil)then - for _, index in ipairs(eventZIndex["other_event"]) do - if (events["other_event"][index] ~= nil) then - for _, value in rpairs(events["other_event"][index]) do - if (value.eventHandler ~= nil) then - if (value:eventHandler(event, ...)) then - return true - end - end - end - end - end - end - if(autoSize)and not(isMonitor)then - if(self.parent==nil)then - if(event=="term_resize")then - self:setSize(termObject.getSize()) - autoSize = true - end - end - end - if(isMonitor)then - if(autoSize)then - if(event=="monitor_resize")then - if(type(monSide)=="string")then - self:setSize(termObject.getSize()) - elseif(type(monSide)=="table")then - for k,v in pairs(monSide)do - for a,b in pairs(v)do - if(p1==b)then - self:setSize(termObject.getSize()) - end - end - end - end - autoSize = true - self:updateDraw() - end - end - if(event == "peripheral")and(p1==monSide)then - if(peripheral.getType(monSide)=="monitor")then - monitorAttached = true - termObject = peripheral.wrap(monSide) - basaltDraw = BasaltDraw(termObject) - self:updateDraw() - end - end - if(event == "peripheral_detach")and(p1==monSide)then - monitorAttached = false - end - end - if(mirrorActive)then - if(peripheral.getType(mirrorSide)=="monitor")then - mirrorAttached = true - basaltDraw.setMirror(peripheral.wrap(mirrorSide)) - end - if(event == "peripheral_detach")and(p1==mirrorSide)then - monitorAttached = false - end - if(event=="monitor_touch")and(mirrorSide==p1)then - self:mouseHandler(1, p2, p3, true) - end - end - end, - - mouseHandler = function(self, button, x, y, _, side) - if(isGroupedMonitor)then - if(termObject.calculateClick~=nil)then - x, y = termObject.calculateClick(side, x, y) - end - end - if(base.mouseHandler(self, button, x, y))then - if(events["mouse_click"]~=nil)then - self:setCursor(false) - for _, index in ipairs(eventZIndex["mouse_click"]) do - if (events["mouse_click"][index] ~= nil) then - for _, value in rpairs(events["mouse_click"][index]) do - if (value.mouseHandler ~= nil) then - if (value:mouseHandler(button, x, y)) then - - return true - end - end - end - end - end - end - if (isMovable) then - local fx, fy = self:getAbsolutePosition(self:getAnchorPosition()) - if (x >= fx) and (x <= fx + self:getWidth() - 1) and (y == fy)then - isDragging = true - dragXOffset = fx - x - dragYOffset = yOff and 1 or 0 - end - end - self:removeFocusedObject() - return true - end - return false - end, - - mouseUpHandler = function(self, button, x, y) - if (isDragging) then - isDragging = false - end - if(base.mouseUpHandler(self, button, x, y))then - if(events["mouse_up"]~=nil)then - for _, index in ipairs(eventZIndex["mouse_up"]) do - if (events["mouse_up"][index] ~= nil) then - for _, value in rpairs(events["mouse_up"][index]) do - if (value.mouseUpHandler ~= nil) then - if (value:mouseUpHandler(button, x, y)) then - return true - end - end - end - end - end - end - return true - end - return false - end, - - scrollHandler = function(self, dir, x, y) - if(base.scrollHandler(self, dir, x, y))then - if(events["mouse_scroll"]~=nil)then - for _, index in pairs(eventZIndex["mouse_scroll"]) do - if (events["mouse_scroll"][index] ~= nil) then - for _, value in rpairs(events["mouse_scroll"][index]) do - if (value.scrollHandler ~= nil) then - if (value:scrollHandler(dir, x, y)) then - return true - end - end - end - end - end - end - local cache = yOffset - if(isScrollable)then - calculateMaxScroll(self) - if(dir>0)or(dir<0)then - yOffset = max(min(yOffset-dir, 0),-scrollAmount) - self:updateDraw() - end - end - if(yOffset==cache)then return false end - self:removeFocusedObject() - return true - end - return false - end, - - hoverHandler = function(self, x, y, stopped) - if(base.hoverHandler(self, x, y, stopped))then - if(events["mouse_move"]~=nil)then - for _, index in pairs(eventZIndex["mouse_move"]) do - if (events["mouse_move"][index] ~= nil) then - for _, value in rpairs(events["mouse_move"][index]) do - if (value.hoverHandler ~= nil) then - if (value:hoverHandler(x, y, stopped)) then - return true - end - end - end - end - end - end - end - return false - end, - - dragHandler = function(self, button, x, y) - if (isDragging) then - local xO, yO = self.parent:getOffsetInternal() - xO = xO < 0 and math.abs(xO) or -xO - yO = yO < 0 and math.abs(yO) or -yO - local parentX = 1 - local parentY = 1 - if (self.parent ~= nil) then - parentX, parentY = self.parent:getAbsolutePosition(self.parent:getAnchorPosition()) - end - self:setPosition(x + dragXOffset - (parentX - 1) + xO, y + dragYOffset - (parentY - 1) + yO) - self:updateDraw() - return true - end - if(self:isVisible())and(self:isEnabled())then - if(events["mouse_drag"]~=nil)then - for _, index in ipairs(eventZIndex["mouse_drag"]) do - if (events["mouse_drag"][index] ~= nil) then - for _, value in rpairs(events["mouse_drag"][index]) do - if (value.dragHandler ~= nil) then - if (value:dragHandler(button, x, y)) then - return true - end - end - end - end - end - end - end - - base.dragHandler(self, button, x, y) - return false - end, - - keyHandler = function(self, key, isHolding) - if (self:isFocused())or(self.parent==nil)then - local val = self:getEventSystem():sendEvent("key", self, "key", key) - if(val==false)then return false end - if(events["key"]~=nil)then - for _, index in pairs(eventZIndex["key"]) do - if (events["key"][index] ~= nil) then - for _, value in rpairs(events["key"][index]) do - if (value.keyHandler ~= nil) then - if (value:keyHandler(key, isHolding)) then - return true - end - end - end - end - end - end - end - return false - end, - - keyUpHandler = function(self, key) - if (self:isFocused())or(self.parent==nil)then - local val = self:getEventSystem():sendEvent("key_up", self, "key_up", key) - if(val==false)then return false end - if(events["key_up"]~=nil)then - for _, index in pairs(eventZIndex["key_up"]) do - if (events["key_up"][index] ~= nil) then - for _, value in rpairs(events["key_up"][index]) do - if (value.keyUpHandler ~= nil) then - if (value:keyUpHandler(key)) then - return true - end - end - end - end - end - end - end - return false - end, - - charHandler = function(self, char) - if (self:isFocused())or(self.parent==nil)then - local val = self:getEventSystem():sendEvent("char", self, "char", char) - if(val==false)then return false end - if(events["char"]~=nil)then - for _, index in pairs(eventZIndex["char"]) do - if (events["char"][index] ~= nil) then - for _, value in rpairs(events["char"][index]) do - if (value.charHandler ~= nil) then - if (value:charHandler(char)) then - return true - end - end - end - end - end - end - end - return false - end, - - setText = function(self, x, y, text) - local obx, oby = self:getAnchorPosition() - if (y >= 1) and (y <= self:getHeight()) then - if (self.parent ~= nil) then - self.parent:setText(max(x + (obx - 1), obx), oby + y - 1, sub(text, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1))) - else - basaltDraw.setText(max(x + (obx - 1), obx), oby + y - 1, sub(text, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1))) - end - end - end; - - setBG = function(self, x, y, bgCol) - local obx, oby = self:getAnchorPosition() - if (y >= 1) and (y <= self:getHeight()) then - if (self.parent ~= nil) then - self.parent:setBG(max(x + (obx - 1), obx), oby + y - 1, sub(bgCol, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1))) - else - basaltDraw.setBG(max(x + (obx - 1), obx), oby + y - 1, sub(bgCol, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1))) - end - end - end; - - setFG = function(self, x, y, fgCol) - local obx, oby = self:getAnchorPosition() - if (y >= 1) and (y <= self:getHeight()) then - if (self.parent ~= nil) then - self.parent:setFG(max(x + (obx - 1), obx), oby + y - 1, sub(fgCol, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1))) - else - basaltDraw.setFG(max(x + (obx - 1), obx), oby + y - 1, sub(fgCol, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1))) - end - end - end; - - writeText = function(self, x, y, text, bgCol, fgCol) - local obx, oby = self:getAnchorPosition() - if (y >= 1) and (y <= self:getHeight()) then - if (self.parent ~= nil) then - self.parent:writeText(max(x + (obx - 1), obx), oby + y - 1, sub(text, max(1 - x + 1, 1), self:getWidth() - x + 1), bgCol, fgCol) - else - basaltDraw.writeText(max(x + (obx - 1), obx), oby + y - 1, sub(text, max(1 - x + 1, 1), max(self:getWidth() - x + 1,1)), bgCol, fgCol) - end - end - end; - - blit = function (self, x, y, t, f, b) - local obx, oby = self:getAnchorPosition() - if (y >= 1) and (y <= self:getHeight()) then - local w = self:getWidth() - if (self.parent ~= nil) then - t = sub(t, max(1 - x + 1, 1), w - x + 1) - f = sub(f, max(1 - x + 1, 1), w - x + 1) - b = sub(b, max(1 - x + 1, 1), w - x + 1) - self.parent:blit(max(x + (obx - 1), obx), oby + y - 1, t, f, b) - else - t = sub(t, max(1 - x + 1, 1), max(w - x + 1,1)) - f = sub(f, max(1 - x + 1, 1), max(w - x + 1,1)) - b = sub(b, max(1 - x + 1, 1), max(w - x + 1,1)) - basaltDraw.blit(max(x + (obx - 1), obx), oby + y - 1, t, f, b) - end - end - end, - - drawBackgroundBox = function(self, x, y, width, height, bgCol) - local obx, oby = self:getAnchorPosition() - - 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)) - if (self.parent ~= nil) then - self.parent:drawBackgroundBox(max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, bgCol) - else - basaltDraw.drawBackgroundBox(max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, bgCol) - end - end; - - drawTextBox = function(self, x, y, width, height, symbol) - local obx, oby = self:getAnchorPosition() - 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)) - if (self.parent ~= nil) then - self.parent:drawTextBox(max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, sub(symbol,1,1)) - else - basaltDraw.drawTextBox(max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, sub(symbol,1,1)) - end - end; - - drawForegroundBox = function(self, x, y, width, height, fgCol) - local obx, oby = self:getAnchorPosition() - 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)) - if (self.parent ~= nil) then - self.parent:drawForegroundBox(max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, fgCol) - else - basaltDraw.drawForegroundBox(max(x + (obx - 1), obx), max(y + (oby - 1), oby), width, height, fgCol) - end - end; - - draw = function(self, force) - if(isMonitor)and not(monitorAttached)then return false end; - if(self.parent==nil)then if(self:getDraw()==false)then return false end end - if (base.draw(self))then - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - local anchx, anchy = self:getAnchorPosition() - local w,h = self:getSize() - if (self.parent == nil) then - if(self.bgColor~=false)then - basaltDraw.drawBackgroundBox(anchx, anchy, w, h, self.bgColor) - basaltDraw.drawTextBox(anchx, anchy, w, h, " ") - end - if(self.fgColor~=false)then basaltDraw.drawForegroundBox(anchx, anchy, w, h, self.fgColor) end - end - if (self.barActive) then - if (self.parent ~= nil) then - self.parent:writeText(anchx, anchy, utils.getTextHorizontalAlign(self.barText, w, self.barTextAlign), self.barBackground, self.barTextcolor) - else - basaltDraw.writeText(anchx, anchy, utils.getTextHorizontalAlign(self.barText, w, self.barTextAlign), self.barBackground, self.barTextcolor) - end - if(self:getBorder("left"))then - if (self.parent ~= nil) then - self.parent:drawBackgroundBox(anchx-1, anchy, 1, 1, self.barBackground) - if(self.bgColor~=false)then - self.parent:drawBackgroundBox(anchx-1, anchy+1, 1, h-1, self.bgColor) - end - end - end - if(self:getBorder("top"))then - if (self.parent ~= nil) then - self.parent:drawBackgroundBox(anchx-1, anchy-1, w+1, 1, self.barBackground) - end - end - end - - for _, index in rpairs(objZIndex) do - if (objects[index] ~= nil) then - for _, value in pairs(objects[index]) do - if (value.draw ~= nil) then - value:draw() - end - end - end - end - end - end; - - updateTerm = function(self) - if(isMonitor)and not(monitorAttached)then return false end; - basaltDraw.update() - end; - - addObject = function(self, obj) - return addObject(obj) - end; - - removeObject = removeObject, - - getObject = function(self, obj) - return getObject(obj) - end; - getDeepObject = function(self, name) - return getDeepObject(name) - end, - - addFrame = function(self, name) - local obj = basalt.newFrame(name or uuid(), self, nil, basalt) - return addObject(obj) - end, - - init = function(self) - if not(initialized)then - if (parent ~= nil) then - base.width, base.height = parent:getSize() - self:setBackground(parent:getTheme("FrameBG")) - self:setForeground(parent:getTheme("FrameText")) - else - base.width, base.height = termObject.getSize() - self:setBackground(basalt.getTheme("BasaltBG")) - self:setForeground(basalt.getTheme("BasaltText")) - end - initialized = true - end - end, - } - for k,v in pairs(_OBJECTS)do - object["add"..k] = function(self, name) - return addObject(v(name or uuid(), self)) - end - end - setmetatable(object, base) - return object +if not term.isColor or not term.isColor() then + error('Basalt requires an advanced (golden) computer to run.', 0) end + +local defaultColors = {} +for k,v in pairs(colors)do + if(type(v)=="number")then + defaultColors[k] = {baseTerm.getPaletteColor(v)} + end +end + + +local function stop() + updaterActive = false + baseTerm.clear() + baseTerm.setCursorPos(1, 1) + for k,v in pairs(colors)do + if(type(v)=="number")then + baseTerm.setPaletteColor(v, colors.packRGB(table.unpack(defaultColors[k]))) + end + end +end + +local function basaltError(errMsg) + baseTerm.clear() + baseTerm.setBackgroundColor(colors.black) + baseTerm.setTextColor(colors.red) + local w,h = baseTerm.getSize() + if(basalt.logging)then + log(errMsg, "Error") + end + + local text = createText("Basalt error: "..errMsg, w) + local yPos = 1 + for k,v in pairs(text)do + baseTerm.setCursorPos(1,yPos) + baseTerm.write(v) + yPos = yPos + 1 + end + baseTerm.setCursorPos(1,yPos+1) + updaterActive = false +end + +local function schedule(f) +assert(f~="function", "Schedule needs a function in order to work!") +return function(...) + local co = coroutine.create(f) + local ok, result = coroutine.resume(co, ...) + if(ok)then + table.insert(schedules, co) + else + basaltError(result) + end + end +end + +local setVariable = function(name, var) + variables[name] = var +end + +local getVariable = function(name) + return variables[name] +end + +local setTheme = function(_theme) + theme = _theme +end + +local getTheme = function(name) + return theme[name] +end + +local bInstance = { + getDynamicValueEventSetting = function() + return basalt.dynamicValueEvents + end, + + getMainFrame = function() + return mainFrame + end, + + setVariable = setVariable, + getVariable = getVariable, + getTheme = getTheme, + + setMainFrame = function(mFrame) + mainFrame = mFrame + end, + + getActiveFrame = function() + return activeFrame + end, + + setActiveFrame = function(aFrame) + activeFrame = aFrame + end, + + getFocusedObject = function() + return focusedObject + end, + + setFocusedObject = function(focused) + focusedObject = focused + end, + + getMonitorFrame = function(name) + return monFrames[name] or monGroups[name][1] + end, + + setMonitorFrame = function(name, frame, isGroupedMon) + if(mainFrame == frame)then mainFrame = nil end + if(isGroupedMon)then + monGroups[name] = {frame, sides} + else + monFrames[name] = frame + end + if(frame==nil)then + monGroups[name] = nil + end + end, + + getBaseTerm = function() + return baseTerm + end, + + schedule = schedule, + + stop = stop, + newFrame = Frame, + + getDirectory = function() + return projectDirectory + end +} + +local function handleSchedules(event, p1, p2, p3, p4) + if(#schedules>0)then + local finished = {} + for n=1,#schedules do + if(schedules[n]~=nil)then + if (coroutine.status(schedules[n]) == "suspended")then + local ok, result = coroutine.resume(schedules[n], event, p1, p2, p3, p4) + if not(ok)then + basaltError(result) + end + else + table.insert(finished, n) + end + end + end + for n=1,#finished do + table.remove(schedules, finished[n]-(n-1)) + end + end +end + +local function drawFrames() + if(updaterActive==false)then return end + if(mainFrame~=nil)then + mainFrame:draw() + mainFrame:updateTerm() + end + for _,v in pairs(monFrames)do + v:draw() + v:updateTerm() + end + for _,v in pairs(monGroups)do + v[1]:draw() + v[1]:updateTerm() + end +end + +local stopped, moveX, moveY = nil, nil, nil +local moveTimer = nil +local function mouseMoveEvent(_, stp, x, y) + stopped, moveX, moveY = stp, x, y + if(moveTimer==nil)then + moveTimer = os.startTimer(moveThrottle/1000) + end +end + +local function moveHandlerTimer() + moveTimer = nil + mainFrame:hoverHandler(moveX, moveY, stopped) + activeFrame = mainFrame +end + +local btn, dragX, dragY = nil, nil, nil +local dragTimer = nil +local function dragHandlerTimer() + dragTimer = nil + mainFrame:dragHandler(btn, dragX, dragY) + activeFrame = mainFrame +end + +local function mouseDragEvent(_, b, x, y) + btn, dragX, dragY = b, x, y + if(dragThrottle<50)then + dragHandlerTimer() + else + if(dragTimer==nil)then + dragTimer = os.startTimer(dragThrottle/1000) + end + end +end + +local function basaltUpdateEvent(event, ...) + local a = {...} + if(basaltEvent:sendEvent("basaltEventCycle", event, ...)==false)then return end + if(event=="terminate")then basalt.stop() end + if(mainFrame~=nil)then + local mouseEvents = { + mouse_click = mainFrame.mouseHandler, + mouse_up = mainFrame.mouseUpHandler, + mouse_scroll = mainFrame.scrollHandler, + mouse_drag = mouseDragEvent, + mouse_move = mouseMoveEvent, + } + local mouseEvent = mouseEvents[event] + if(mouseEvent~=nil)then + mouseEvent(mainFrame, ...) + drawFrames() + return + end + end + + if(event == "monitor_touch") then + if(monFrames[a[1]]~=nil)then + monFrames[a[1]]:mouseHandler(1, a[2], a[3], true) + activeFrame = monFrames[a[1]] + end + if(count(monGroups)>0)then + for k,v in pairs(monGroups)do + v[1]:mouseHandler(1, a[2], a[3], true, a[1]) + end + end + drawFrames() + return + end + + if(activeFrame~=nil)then + local keyEvents = { + char = activeFrame.charHandler, + key = activeFrame.keyHandler, + key_up = activeFrame.keyUpHandler, + } + local keyEvent = keyEvents[event] + if(keyEvent~=nil)then + if(event == "key")then + activeKey[a[1]] = true + elseif(event == "key_up")then + activeKey[a[1]] = false + end + keyEvent(activeFrame, ...) + drawFrames() + return + end + end + + if(event=="timer")and(a[1]==moveTimer)then + moveHandlerTimer() + elseif(event=="timer")and(a[1]==dragTimer)then + dragHandlerTimer() + else + for k, v in pairs(frames) do + v:eventHandler(event, ...) + end + end + handleSchedules(event, ...) + drawFrames() +end + +basalt = { + logging = false, + dynamicValueEvents = false, + setTheme = setTheme, + getTheme = getTheme, + drawFrames = drawFrames, + getVersion = function() + return version + end, + + setVariable = setVariable, + getVariable = getVariable, + + setBaseTerm = function(_baseTerm) + baseTerm = _baseTerm + end, + + log = function(...) + log(...) + end, + + setMouseMoveThrottle = function(amount) + if(_HOST:find("CraftOS%-PC"))then + if(config.get("mouse_move_throttle")~=10)then config.set("mouse_move_throttle", 10) end + if(amount<100)then + moveThrottle = 100 + else + moveThrottle = amount + end + return true + end + return false + end, + + setMouseDragThrottle = function(amount) + if(amount<=0)then + dragThrottle = 0 + else + dragTimer = nil + dragThrottle = amount + end + end, + + autoUpdate = function(isActive) + updaterActive = isActive + if(isActive==nil)then updaterActive = true end + local function f() + drawFrames() + while updaterActive do + basaltUpdateEvent(os.pullEventRaw()) + end + end + local ok, err = xpcall(f, debug.traceback) + if not(ok)then + basaltError(err) + return + end + end, + + update = function(event, ...) + if (event ~= nil) then + local ok, err = xpcall(basaltUpdateEvent, debug.traceback, event, ...) + if not(ok)then + basaltError(err) + return + end + end + end, + + stop = stop, + stopUpdate = stop, + + isKeyDown = function(key) + if(activeKey[key]==nil)then return false end + return activeKey[key]; + end, + + getFrame = function(name) + for _, value in pairs(frames) do + if (value.name == name) then + return value + end + end + end, + + getActiveFrame = function() + return activeFrame + end, + + setActiveFrame = function(frame) + if (frame:getType() == "Frame") then + activeFrame = frame + return true + end + return false + end, + + onEvent = function(...) + for _,v in pairs(table.pack(...))do + if(type(v)=="function")then + basaltEvent:registerEvent("basaltEventCycle", v) + end + end + end, + + schedule = schedule, + + createFrame = function(name) + name = name or uuid() + for _, v in pairs(frames) do + if (v.name == name) then + return nil + end + end + local newFrame = Frame(name,nil,nil,bInstance) + newFrame:init() + table.insert(frames, newFrame) + if(mainFrame==nil)and(newFrame:getName()~="basaltDebuggingFrame")then + newFrame:show() + end + return newFrame + end, + + removeFrame = function(name) + frames[name] = nil + end, + + setProjectDir = function(dir) + projectDirectory = dir + end, + + debug = function(...) + local args = { ... } + if(mainFrame==nil)then print(...) return end + if (mainFrame.name ~= "basaltDebuggingFrame") then + if (mainFrame ~= basalt.debugFrame) then + basalt.debugLabel:setParent(mainFrame) + end + end + local str = "" + for key, value in pairs(args) do + str = str .. tostring(value) .. (#args ~= key and ", " or "") + end + basalt.debugLabel:setText("[Debug] " .. str) + for k,v in pairs(createText(str, basalt.debugList:getWidth()))do + basalt.debugList:addItem(v) + end + if (basalt.debugList:getItemCount() > 50) then + basalt.debugList:removeItem(1) + end + basalt.debugList:setValue(basalt.debugList:getItem(basalt.debugList:getItemCount())) + if(basalt.debugList.getItemCount() > basalt.debugList:getHeight())then + basalt.debugList:setOffset(basalt.debugList:getItemCount() - basalt.debugList:getHeight()) + end + basalt.debugLabel:show() + end, +} + +basalt.debugFrame = basalt.createFrame("basaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setBar("Debug", colors.black, colors.gray) +basalt.debugFrame:addButton("back"):setAnchor("topRight"):setSize(1, 1):setText("\22"):onClick(function() if(basalt.oldFrame~=nil)then basalt.oldFrame:show() end end):setBackground(colors.red):show() +basalt.debugList = basalt.debugFrame:addList("debugList"):setSize("parent.w - 2", "parent.h - 3"):setPosition(2, 3):setScrollable(true):show() +basalt.debugLabel = basalt.debugFrame:addLabel("debugLabel"):onClick(function() basalt.oldFrame = mainFrame basalt.debugFrame:show() end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottomLeft"):ignoreOffset():setZIndex(20):show() + +return basalt diff --git a/docs/home/How-To.md b/docs/home/How-To.md index f28754b..ebc0ced 100644 --- a/docs/home/How-To.md +++ b/docs/home/How-To.md @@ -1,3 +1,5 @@ +# How-To + After downloading the project you can finally start creating your own program and use basalt. The first thing you want to use in your program is always: ```lua @@ -24,7 +26,7 @@ local basalt = require("basalt") --> Load the basalt framework into the variable --> Now we want to create a base frame, we call the variable "main" - by default everything you create is visible. (you don't need to use :show()) local main = basalt.createFrame() -local button = mainFrame:addButton() --> Here we add our first button +local button = main:addButton() --> Here we add our first button button:setPosition(4, 4) -- of course we want to change the default position of our button button:setSize(16, 3) -- and the default size. button:setText("Click me!") --> This method displays what the text of our button should look like