local Event = require("gfx.event") local Logger = require("logging").getGlobalLogger() local Element = { x = 1, y = 1, width = 1, height = 1, bgColor = colors.black, fgColor = colors.white, window = nil, parent = nil, visible = true, dirty = true, id = "", onClick = nil, onKey = nil, onChar = nil, onEvent = nil } Element.__index = Element function Element:new(o) local obj = o or {} setmetatable(obj, self) obj.__index = obj if obj.parent then obj:_reload() end return obj end function Element:draw() Logger:trace("draw") local dirty = self:_isDirty() if dirty then local win = self:_getWindow() -- Calling draw() without a valid window is a logical error ---@diagnostic disable-next-line: need-check-nil win.setCursorPos(1, 1) end self.dirty = false return dirty end function Element:getX() return self.x end function Element:getY() return self.y end function Element:setPos(x, y) Logger:trace("setPos", x, y) if (x ~= nil and self.x ~= x) or (y ~= nil and self.y ~= y) then self.x = x or self.x self.y = y or self.y self:setDirty() self:_reload() end end function Element:setX(x) if self.x ~= x then self.x = x self:setDirty() self:_reload() end end function Element:setY(y) if self.y ~= y then self.y = y self:setDirty() self:_reload() end end function Element:setParent(parent) Logger:trace("setParent", self:getId(), parent, self.parent ~= parent) if self.parent ~= parent then self.parent = parent self:setDirty() self:_reload() end end function Element:setFgColor(color) if color ~= self.fgColor then self.fgColor = color self:setDirty() local win = self:_getWindow() if win ~= nil then win.setTextColor(color) end end end function Element:setBgColor(color) if color ~= self.bgColor then self.bgColor = color self:setDirty() local win = self:_getWindow() if win ~= nil then win.setBackgroundColor(color) end end end function Element:getFgColor() return self.fgColor end function Element:getBgColor() return self.bgColor end function Element:_getWindow() return self.window end function Element:_setWindow(window) self.window = window end function Element:setVisible(visible) self.visible = visible self:setDirty() local win = self:_getWindow() if win ~= nil then win.setVisible(visible) end end function Element:_isDirty() return self.dirty end function Element:resize(opts) if (opts.width ~= nil and self.x ~= opts.width) or (opts.height ~= nil and self.y ~= opts.height) then self.width = opts.width or self.width self.height = opts.height or self.height self:setDirty() self:_reload() end end function Element:setWidth(width) if width ~= self.width then self.width = width self:setDirty() self:_reload() end end function Element:setHeight(height) if height ~= self.height then self.height = height self:setDirty() self:_reload() end end function Element:getWidth() return self.width end function Element:getHeight() return self.height end function Element:getPos() return self.x, self.y end function Element:isVisible() return self.visible end function Element:setDirty(fullInvalidate) self.dirty = true end function Element:findById(id) if self:getId() == id then return self else return nil end end function Element:getId() return self.id end function Element:handleEvent(evt) if Event.isClickEvent(evt) and Event.containsClick(self, evt) and self.onClick ~= nil then local x, y, source = Event.getClickParams(evt) return not not self.onClick(self, x, y, source) elseif Event.isKeyEvent(evt) and self.onKey ~= nil then local key, held = Event.getKeyParams(evt) return not not self.onKey(self, key, held) elseif Event.isCharEvent(evt) and self.onChar ~= nil then local keyChar = Event.getCharValue(evt) return not not self.onChar(self, keyChar) end return self.onEvent ~= nil and self.onEvent(self, evt) end function Element:setOnClick(onClick) self.onClick = onClick end function Element:_reload() Logger:trace("_reload", self:getId(), self:getX(), self:getY(), self:getWidth(), self:getHeight(), self:isVisible(), Logger.plain({bg=self:getBgColor(), fg=self:getFgColor()})) if self.parent ~= nil then local win = window.create(self.parent, self:getX(), self:getY(), self:getWidth(), self:getHeight(), self:isVisible()) win.setBackgroundColor(self:getBgColor()) win.setTextColor(self:getFgColor()) win.setVisible(self.visible) self:_setWindow(win) self:setDirty(true) end end return Element