local GUI = {} local EMPTY = {} local Vector2 = require("Vector2") local templates = {} GUI.__index = GUI local function clone(data) if type(data) ~= "table" then return data else local new = {} for k, v in pairs(data) do new[k] = clone(v) end return setmetatable(new, getmetatable(data)) end end function revipairs(t) return function(t, i) i = i - 1 if i > 0 then return i, t[i] end end, t, #t+1 end function GUI.sortByDepth(a, b) return a.depth < b.depth end function getPos(self) if self._parent then local x0, y0 = self._parent:getPos() return self.pos.x + x0, self.pos.y + y0 else return self.pos.x, self.pos.y end end function getSize(self) return self.size.x, self.size.y end function getBounds(self) local x, y = getPos(self) return x, y, self.size.x, self.size.y end function inBounds(self, x, y) local Ex, Ey, width, height = getBounds(self) return (x > Ex and x < Ex + width and y > Ey and y < Ey + height) end function GUI.createElement(elementType, data) assert(templates[elementType], "Element type \""..elementType.."\" dosen't exist") local elem = data or {} for k, v in pairs(templates[elementType]) do if not (type(k) == "string" and k:find("^__")) and type(elem[k]) == "nil" then elem[k] = clone(v) end end setmetatable(elem, template) -- Ensure that the mandatory varaibles are declered elem.depth = elem.depth or 0 elem.pos = elem.pos or Vector2() elem.size = elem.size or Vector2() if type(elem.visible) ~= "boolean" then elem.visible = true end if elem.load then elem:load() end return elem end function GUI.new(class, width, height) assert(width and height, "Width and height are undefined") local self = setmetatable({}, class) self.pos = Vector2() self.size = Vector2(width, height) self.getPos = getPos self.getSize = getSize self.getBounds = getBounds self.inBounds = inBounds self._elements = {} return self end function GUI:removeElement(element) for i, elem in ipairs(self._elements) do if elem == element then table.remove(self._elements, i) break end end end function GUI:addElement(elementType, data) local elem = GUI.createElement(elementType, data) elem._parent = self -- Add it to the list table.insert(self._elements, elem) table.sort(self._elements, GUI.sortByDepth) return elem end function GUI.newTemplate(name) local template = {} template.__index = template template.getPos = getPos template.getSize = getSize template.getBounds = getBounds template.inBounds = inBounds templates[name] = template return template end function GUI:resize(...) for _, element in ipairs(self._elements) do if element.resize then element:resize(...) end end end function GUI.callMethod(parent, name, ...) local useFallback = false for _, element in revipairs(parent._elements) do if element.visible then if type(element._elements) == "table" then if GUI.callMethod(element, name, ...) then break end end if useFallback and element[name.."Fallback"] then element[name.."Fallback"](element, ...) elseif element[name] then if element[name](element, ...) then useFallback = true end end end end return useFallback end function GUI:draw(...) local useFallback = false for _, element in ipairs(self._elements) do if element.visible then if useFallback and element["drawFallback"] then element["drawFallback"](element, ...) elseif element["draw"] then if element["draw"](element, ...) then useFallback = true end end if type(element._elements) == "table" then GUI.draw(element, "draw", ...) end end end return useFallback end for i, name in pairs{"keypressed", "keyreleased", "mousemoved", "mousepressed", "mousereleased", "textinput"} do GUI[name] = function(self, ...) GUI.callMethod(self, name, ...) end end return GUI