diff --git a/.github/workflows/config.yml b/.github/workflows/main.yml similarity index 60% rename from .github/workflows/config.yml rename to .github/workflows/main.yml index 22e8052..1f98cf4 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Update Installer Config +name: Basalt Build Pipeline on: push: @@ -8,7 +8,7 @@ on: - 'src/**' jobs: - update-config: + build: runs-on: ubuntu-latest permissions: contents: write @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Install Lua run: | @@ -25,11 +27,17 @@ jobs: - name: Generate Config run: | lua tools/generate-config.lua - - - name: Commit and Push Changes + + - name: Generate Changelog + id: changelog + uses: heinrichreimer/github-changelog-generator-action@v2.3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Commit Changes run: | git config --global user.name 'github-actions[bot]' git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add config.lua - git commit -m "Update installer config" || exit 0 + git add config.lua CHANGELOG.md + git commit -m "Update config and changelog" || exit 0 git push \ No newline at end of file diff --git a/src/elementManager.lua b/src/elementManager.lua index 039d25d..5c31e29 100644 --- a/src/elementManager.lua +++ b/src/elementManager.lua @@ -98,9 +98,7 @@ function ElementManager.loadElement(name) for funcName, func in pairs(plugin) do if funcName ~= "setup" and funcName ~= "hooks" then - element[funcName] = function(self, ...) - return func(self, ...) - end + element[funcName] = func end end end diff --git a/src/elements/BaseElement.lua b/src/elements/BaseElement.lua index 3de2a45..a101c44 100644 --- a/src/elements/BaseElement.lua +++ b/src/elements/BaseElement.lua @@ -46,9 +46,8 @@ end --- @param basalt table The basalt instance --- @return table The newly created BaseElement instance --- @usage local element = BaseElement.new("myId", basalt) -function BaseElement.new(props, basalt) +function BaseElement.new() local self = setmetatable({}, BaseElement):__init() - self:init(props, basalt) return self end @@ -57,11 +56,7 @@ end --- @param basalt table The basalt instance --- @return table self The initialized instance function BaseElement:init(props, basalt) - if(type(props) == "table")then - for k,v in pairs(props)do - self[k] = v - end - end + self._props = props self._values.id = uuid() self.basalt = basalt self._registeredEvents = {} @@ -80,6 +75,18 @@ function BaseElement:init(props, basalt) return self end +--- Post initialization hook +--- @return table self The BaseElement instance +function BaseElement:postInit() + if(self._props)then + for k,v in pairs(self._props)do + self.set(k, v) + end + end + self._props = nil + return self +end + --- Checks if the element is a specific type --- @param type string The type to check for --- @return boolean Whether the element is of the specified type diff --git a/src/elements/BaseFrame.lua b/src/elements/BaseFrame.lua index 5542dca..df999bd 100644 --- a/src/elements/BaseFrame.lua +++ b/src/elements/BaseFrame.lua @@ -19,11 +19,10 @@ BaseFrame.defineProperty(BaseFrame, "term", {default = nil, type = "table", sett return value end}) -function BaseFrame.new(props, basalt) +function BaseFrame.new() local self = setmetatable({}, BaseFrame):__init() self.set("term", term.current()) self.set("background", colors.lightGray) - self:init(props, basalt) return self end diff --git a/src/elements/Button.lua b/src/elements/Button.lua index 344ffb7..a6c6b32 100644 --- a/src/elements/Button.lua +++ b/src/elements/Button.lua @@ -13,12 +13,11 @@ Button.defineProperty(Button, "text", {default = "Button", type = "string", canT Button.listenTo(Button, "mouse_click") Button.listenTo(Button, "mouse_up") -function Button.new(props, basalt) +function Button.new() local self = setmetatable({}, Button):__init() self.set("width", 10) self.set("height", 3) self.set("z", 5) - self:init(props, basalt) return self end diff --git a/src/elements/Checkbox.lua b/src/elements/Checkbox.lua index 1df8027..8a6b685 100644 --- a/src/elements/Checkbox.lua +++ b/src/elements/Checkbox.lua @@ -13,11 +13,10 @@ Checkbox.defineProperty(Checkbox, "symbol", {default = "x", type = "string"}) Checkbox.listenTo(Checkbox, "mouse_click") -function Checkbox.new(props, basalt) +function Checkbox.new() local self = setmetatable({}, Checkbox):__init() self.set("width", 1) self.set("height", 1) - self:init(props, basalt) return self end diff --git a/src/elements/Container.lua b/src/elements/Container.lua index ca59d26..27364c6 100644 --- a/src/elements/Container.lua +++ b/src/elements/Container.lua @@ -57,6 +57,7 @@ for k, _ in pairs(elementManager:getElementList()) do expect(1, self, "table") local element = self.basalt.create(k, ...) self:addChild(element) + element:postInit() return element end Container["addDelayed"..capitalizedName] = function(self, prop) @@ -67,9 +68,8 @@ for k, _ in pairs(elementManager:getElementList()) do end end -function Container.new(props, basalt) +function Container.new() local self = setmetatable({}, Container):__init() - self:init(props, basalt) return self end diff --git a/src/elements/Dropdown.lua b/src/elements/Dropdown.lua index 8ed2ae6..c041256 100644 --- a/src/elements/Dropdown.lua +++ b/src/elements/Dropdown.lua @@ -11,12 +11,11 @@ Dropdown.defineProperty(Dropdown, "dropdownHeight", {default = 5, type = "number Dropdown.defineProperty(Dropdown, "selectedText", {default = "", type = "string"}) Dropdown.defineProperty(Dropdown, "dropSymbol", {default = "\31", type = "string"}) -- ▼ Symbol -function Dropdown.new(props, basalt) +function Dropdown.new() local self = setmetatable({}, Dropdown):__init() self.set("width", 16) - self.set("height", 1) -- Dropdown ist initial nur 1 Zeile hoch + self.set("height", 1) self.set("z", 8) - self:init(props, basalt) return self end diff --git a/src/elements/Flexbox.lua b/src/elements/Flexbox.lua index f9ecd21..b0e5524 100644 --- a/src/elements/Flexbox.lua +++ b/src/elements/Flexbox.lua @@ -226,11 +226,9 @@ local function updateLayout(self, direction, spacing, justifyContent, wrap) end --- Creates a new Flexbox instance ---- @param props table The properties to initialize the element with ---- @param basalt table The basalt instance --- @return Flexbox object The newly created Flexbox instance --- @usage local element = Flexbox.new("myId", basalt) -function Flexbox.new(props, basalt) +function Flexbox.new() local self = setmetatable({}, Flexbox):__init() self.set("width", 12) self.set("height", 6) @@ -238,7 +236,6 @@ function Flexbox.new(props, basalt) self.set("z", 10) self:observe("width", function() self.set("flexUpdateLayout", true) end) self:observe("height", function() self.set("flexUpdateLayout", true) end) - self:init(props, basalt) return self end diff --git a/src/elements/Frame.lua b/src/elements/Frame.lua index 128e29c..1a4973a 100644 --- a/src/elements/Frame.lua +++ b/src/elements/Frame.lua @@ -6,17 +6,14 @@ local Frame = setmetatable({}, Container) Frame.__index = Frame --- Creates a new Frame instance ---- @param props table The properties to initialize the element with ---- @param basalt table The basalt instance --- @return Frame object The newly created Frame instance --- @usage local element = Frame.new("myId", basalt) -function Frame.new(props, basalt) +function Frame.new() local self = setmetatable({}, Frame):__init() self.set("width", 12) self.set("height", 6) self.set("background", colors.gray) self.set("z", 10) - self:init(props, basalt) return self end diff --git a/src/elements/Input.lua b/src/elements/Input.lua index ac9df0c..4954699 100644 --- a/src/elements/Input.lua +++ b/src/elements/Input.lua @@ -26,15 +26,12 @@ Input.listenTo(Input, "key") Input.listenTo(Input, "char") --- Creates a new Input instance ---- @param props table The properties to initialize the element with ---- @param basalt table The basalt instance --- @return Input object The newly created Input instance --- @usage local element = Input.new("myId", basalt) -function Input.new(props, basalt) +function Input.new() local self = setmetatable({}, Input):__init() self.set("width", 8) self.set("z", 3) - self:init(id, basalt) return self end diff --git a/src/elements/Label.lua b/src/elements/Label.lua index 778d887..e68fca8 100644 --- a/src/elements/Label.lua +++ b/src/elements/Label.lua @@ -13,16 +13,13 @@ Label.defineProperty(Label, "text", {default = "Label", type = "string", setter end}) --- Creates a new Label instance ---- @param props table The properties to initialize the element with ---- @param basalt table The basalt instance --- @return Label object The newly created Label instance --- @usage local element = Label.new("myId", basalt) -function Label.new(props, basalt) +function Label.new() local self = setmetatable({}, Label):__init() self.set("z", 3) self.set("foreground", colors.black) self.set("backgroundEnabled", false) - self:init(props, basalt) return self end diff --git a/src/elements/List.lua b/src/elements/List.lua index 5089806..648ee45 100644 --- a/src/elements/List.lua +++ b/src/elements/List.lua @@ -18,12 +18,11 @@ List.defineProperty(List, "selectedColor", {default = colors.blue, type = "numbe List.listenTo(List, "mouse_click") List.listenTo(List, "mouse_scroll") -function List.new(props, basalt) +function List.new() local self = setmetatable({}, List):__init() self.set("width", 16) self.set("height", 8) self.set("background", colors.gray) - self:init(props, basalt) return self end diff --git a/src/elements/Menu.lua b/src/elements/Menu.lua index 555f6ba..c59dc5d 100644 --- a/src/elements/Menu.lua +++ b/src/elements/Menu.lua @@ -8,12 +8,11 @@ Menu.__index = Menu Menu.defineProperty(Menu, "separatorColor", {default = colors.gray, type = "number"}) -function Menu.new(props, basalt) +function Menu.new() local self = setmetatable({}, Menu):__init() self.set("width", 30) self.set("height", 1) self.set("background", colors.gray) - self:init(props, basalt) return self end diff --git a/src/elements/Program.lua b/src/elements/Program.lua index d426c4a..081750f 100644 --- a/src/elements/Program.lua +++ b/src/elements/Program.lua @@ -108,16 +108,13 @@ function BasaltProgram:stop() end --- Creates a new Program instance ---- @param props table The properties to initialize the element with ---- @param basalt table The basalt instance --- @return Program object The newly created Program instance --- @usage local element = Program.new("myId", basalt) -function Program.new(props, basalt) +function Program.new() local self = setmetatable({}, Program):__init() self.set("z", 5) self.set("width", 30) self.set("height", 12) - self:init(props, basalt) return self end diff --git a/src/elements/ProgressBar.lua b/src/elements/ProgressBar.lua index f305b85..6ded9c6 100644 --- a/src/elements/ProgressBar.lua +++ b/src/elements/ProgressBar.lua @@ -11,11 +11,10 @@ ProgressBar.defineProperty(ProgressBar, "showPercentage", {default = false, type ---@property progressColor color Progress bar color ProgressBar.defineProperty(ProgressBar, "progressColor", {default = colors.lime, type = "number"}) -function ProgressBar.new(props, basalt) +function ProgressBar.new() local self = setmetatable({}, ProgressBar):__init() self.set("width", 10) self.set("height", 1) - self:init(props, basalt) return self end diff --git a/src/elements/Slider.lua b/src/elements/Slider.lua index 0320c8f..dd6c925 100644 --- a/src/elements/Slider.lua +++ b/src/elements/Slider.lua @@ -19,12 +19,11 @@ Slider.listenTo(Slider, "mouse_click") Slider.listenTo(Slider, "mouse_drag") Slider.listenTo(Slider, "mouse_up") -function Slider.new(props, basalt) +function Slider.new() local self = setmetatable({}, Slider):__init() self.set("width", 8) self.set("height", 1) self.set("backgroundEnabled", false) - self:init(props, basalt) return self end diff --git a/src/elements/Table.lua b/src/elements/Table.lua index 8a9a893..e3b364d 100644 --- a/src/elements/Table.lua +++ b/src/elements/Table.lua @@ -18,12 +18,11 @@ Table.defineProperty(Table, "scrollOffset", {default = 0, type = "number", canTr Table.listenTo(Table, "mouse_click") Table.listenTo(Table, "mouse_scroll") -function Table.new(props, basalt) +function Table.new() local self = setmetatable({}, Table):__init() self.set("width", 30) self.set("height", 10) self.set("z", 5) - self:init(props, basalt) return self end diff --git a/src/elements/Tree.lua b/src/elements/Tree.lua index e94db5f..4d39a61 100644 --- a/src/elements/Tree.lua +++ b/src/elements/Tree.lua @@ -15,12 +15,11 @@ Tree.defineProperty(Tree, "selectedColor", {default = colors.lightBlue, type = " Tree.listenTo(Tree, "mouse_click") Tree.listenTo(Tree, "mouse_scroll") -function Tree.new(props, basalt) +function Tree.new() local self = setmetatable({}, Tree):__init() self.set("width", 30) self.set("height", 10) self.set("z", 5) - self:init(props, basalt) return self end diff --git a/src/elements/VisualElement.lua b/src/elements/VisualElement.lua index 108a18e..c135c57 100644 --- a/src/elements/VisualElement.lua +++ b/src/elements/VisualElement.lua @@ -75,9 +75,8 @@ local max, min = math.max, math.min --- @param basalt table The basalt instance --- @return VisualElement object The newly created VisualElement instance --- @usage local element = VisualElement.new("myId", basalt) -function VisualElement.new(props, basalt) +function VisualElement.new() local self = setmetatable({}, VisualElement):__init() - self:init(props, basalt) return self end diff --git a/src/libraries/utils.lua b/src/libraries/utils.lua index 7b3206e..ff7d726 100644 --- a/src/libraries/utils.lua +++ b/src/libraries/utils.lua @@ -24,6 +24,22 @@ function utils.deepCopy(obj) return copy end +function utils.copy(obj) + local new = {} + for k,v in pairs(obj)do + new[k] = v + end + return new +end + +function utils.reverse(t) + local reversed = {} + for i = #t, 1, -1 do + table.insert(reversed, t[i]) + end + return reversed +end + function utils.uuid() return string.format('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', math.random(0, 0xffff), math.random(0, 0xffff), math.random(0, 0xffff), diff --git a/src/main.lua b/src/main.lua index ce94c05..c801db1 100644 --- a/src/main.lua +++ b/src/main.lua @@ -73,7 +73,9 @@ function basalt.create(type, properties, lazyLoading, parent) queueLazyElements() return blueprint else - return elementClass.new(properties, basalt) + local element = elementClass.new() + element:init(properties, basalt) + return element end end @@ -82,6 +84,7 @@ end --- @usage local mainFrame = basalt.createFrame() function basalt.createFrame() local frame = basalt.create("BaseFrame") + frame:postInit() mainFrame = frame return frame end diff --git a/src/plugins/debug.lua b/src/plugins/debug.lua index c29212b..a83532b 100644 --- a/src/plugins/debug.lua +++ b/src/plugins/debug.lua @@ -55,20 +55,19 @@ local BaseFrame = { if not self._debugFrame then local width = self.get("width") local height = self.get("height") - self._debugFrame = self:addFrame() + self._debugFrame = self:addFrame("basaltDebugLog") :setWidth(width) :setHeight(height) - :setBackground(colors.black) :setZ(999) :listenEvent("mouse_scroll", true) + self.basalt.LOGGER.debug("Created debug log frame " .. self._debugFrame.get("name")) - self._debugFrame:addButton() + self._debugFrame:addButton("basaltDebugLogClose") :setWidth(9) :setHeight(1) :setX(width - 8) :setY(height) :setText("Close") - :setBackground(colors.red) :onMouseClick(function() self:hideDebugLog() end) diff --git a/src/plugins/theme.lua b/src/plugins/theme.lua index 572a5dc..247d6e6 100644 --- a/src/plugins/theme.lua +++ b/src/plugins/theme.lua @@ -8,30 +8,29 @@ local defaultTheme = { foreground = colors.black, Frame = { - background = colors.gray, - - Button = { - background = "{self.clicked and colors.black or colors.blue}", - foreground = "{self.clicked and colors.blue or colors.white}" - } + background = colors.black, + names = { + basaltDebugLogClose = { + background = colors.blue, + foreground = colors.white + } + }, }, - Button = { background = "{self.clicked and colors.black or colors.cyan}", - foreground = "{self.clicked and colors.cyan or colors.black}" + foreground = "{self.clicked and colors.cyan or colors.black}", }, - Input = { - background = "{self.focused and colors.cyan or colors.lightGray}", - foreground = colors.black, - placeholderColor = colors.gray + names = { + basaltDebugLog = { + background = colors.red, + foreground = colors.white + }, + test = { + background = "{self.clicked and colors.black or colors.green}", + foreground = "{self.clicked and colors.green or colors.black}" + } }, - - List = { - background = colors.cyan, - foreground = colors.black, - selectedColor = colors.blue - } } } @@ -41,115 +40,129 @@ local themes = { local currentTheme = "default" -local function resolveThemeValue(value, theme) - if type(value) == "string" and theme.colors[value] then - return theme.colors[value] +local BaseElement = { + hooks = { + postInit = { + pre = function(self) + self:applyTheme() + end} + } +} + +function BaseElement.____getElementPath(self, types) + if types then + table.insert(types, 1, self._values.type) + else + types = {self._values.type} + end + local parent = self.parent + if parent then + return parent.____getElementPath(parent, types) + else + return types end - return value end -local function getThemeForElement(element) - local path = {} - local current = element +local function lookUpTemplate(theme, path) + local current = theme - while current do - table.insert(path, 1, current.get("type")) - current = current.parent - end + for i = 1, #path do + local found = false + local types = path[i] - local result = {} - local current = defaultTheme - - for _, elementType in ipairs(path) do - if current[elementType] then - for k,v in pairs(current[elementType]) do - result[k] = v + for _, elementType in ipairs(types) do + if current[elementType] then + current = current[elementType] + found = true + break end - current = current[elementType] + end + + if not found then + return nil end end + return current +end + +local function getDefaultProperties(theme, elementType) + local result = {} + if theme.default then + for k,v in pairs(theme.default) do + if type(v) ~= "table" then + result[k] = v + end + end + + if theme.default[elementType] then + for k,v in pairs(theme.default[elementType]) do + if type(v) ~= "table" then + result[k] = v + end + end + end + end + return result +end + +local function applyNamedStyles(result, theme, elementType, elementName, themeTable) + if theme.default and theme.default.names and theme.default.names[elementName] then + for k,v in pairs(theme.default.names[elementName]) do + if type(v) ~= "table" then result[k] = v end + end + end + + if theme.default and theme.default[elementType] and theme.default[elementType].names + and theme.default[elementType].names[elementName] then + for k,v in pairs(theme.default[elementType].names[elementName]) do + if type(v) ~= "table" then result[k] = v end + end + end + + if themeTable and themeTable.names and themeTable.names[elementName] then + for k,v in pairs(themeTable.names[elementName]) do + if type(v) ~= "table" then result[k] = v end + end + end +end + +local function collectThemeProps(theme, path, elementType, elementName) + local result = {} + local themeTable = lookUpTemplate(theme, path) + if themeTable then + for k,v in pairs(themeTable) do + if type(v) ~= "table" then + result[k] = v + end + end + end + + if next(result) == nil then + result = getDefaultProperties(theme, elementType) + end + + applyNamedStyles(result, theme, elementType, elementName, themeTable) + return result end -local function applyTheme(element, props) - - local theme = getThemeForElement(element) - - if props then - for k,v in pairs(props) do - theme[k] = v - end - end - - for k,v in pairs(theme) do - if element:getPropertyConfig(k) then - element.set(k, v) - end - end -end - -local BaseElement = { - hooks = { - init = function(self) - self.defineProperty(self, "theme", { - default = currentTheme, - type = "string", - setter = function(self, value) - self:applyTheme(value) - return value - end - }) - end - } -} - -function BaseElement:applyTheme(themeName) - local theme = themes[themeName] or themes.default - local elementType = self.get("type") - - if theme.elementStyles[elementType] then - local styles = theme.elementStyles[elementType] + function BaseElement:applyTheme() + local styles = self:getTheme() + if(styles ~= nil) then for prop, value in pairs(styles) do - if self:getPropertyConfig(prop) then - self.set(prop, resolveThemeValue(value, theme)) - end + self.set(prop, value) end end end -local BaseFrame = { - hooks = { - init = function(self) - applyTheme(self) - end - } -} +function BaseElement:getTheme() + local path = self:____getElementPath() + local elementType = self.get("type") + local elementName = self.get("name") -local Container = { - hooks = { - init = function(self) - for k, _ in pairs(self.basalt.getElementManager().getElementList()) do - local capitalizedName = k:sub(1,1):upper() .. k:sub(2) - if capitalizedName ~= "BaseFrame" then - local methodName = "add"..capitalizedName - local original = self[methodName] - if original then - self[methodName] = function(self, name, props) - if type(name) == "table" then - props = name - name = nil - end - local element = original(self, name) - applyTheme(element, props) - return element - end - end - end - end - end - } -} + return collectThemeProps(themes[currentTheme], path, elementType, elementName) +end local themeAPI = { setTheme = function(newTheme) @@ -171,13 +184,7 @@ local themeAPI = { } local Theme = { - setup = function(basalt) - basalt.setTheme(defaultTheme) - end, - BaseElement = BaseElement, - BaseFrame = BaseFrame, - Container = Container, API = themeAPI } diff --git a/src/propertySystem.lua b/src/propertySystem.lua index dd75244..e498424 100644 --- a/src/propertySystem.lua +++ b/src/propertySystem.lua @@ -114,7 +114,8 @@ function PropertySystem.blueprint(elementClass, properties, basalt, parent) end template.create = function(self) - local element = elementClass.new({}, basalt) + local element = elementClass.new() + element:init({}, self.basalt) for name, value in pairs(self._values) do element._values[name] = value end @@ -128,6 +129,7 @@ function PropertySystem.blueprint(elementClass, properties, basalt, parent) end element:updateRender() self.loadedCallback(element) + element:postInit() return element end diff --git a/tools/generate-config.lua b/tools/generate-config.lua index c4d6ce5..d06d31e 100644 --- a/tools/generate-config.lua +++ b/tools/generate-config.lua @@ -1,27 +1,3 @@ -local function serialize(t, indent) - indent = indent or "" - local result = "{\n" - for k, v in pairs(t) do - result = result .. indent .. " " - - if type(k) == "string" then - result = result .. "[\"" .. k .. "\"] = " - else - result = result .. "[" .. k .. "] = " - end - - if type(v) == "table" then - result = result .. serialize(v, indent .. " ") - elseif type(v) == "string" then - result = result .. "\"" .. v .. "\"" - else - result = result .. tostring(v) - end - result = result .. ",\n" - end - return result .. indent .. "}" -end - local function scanDir(dir) local files = {} for file in io.popen('find "'..dir..'" -type f -name "*.lua"'):lines() do @@ -47,5 +23,5 @@ local config = { } local f = io.open("config.lua", "w") -f:write("return " .. serialize(config)) +f:write("return " .. textutils.serialize(config)) f:close() diff --git a/tools/markdown.lua b/tools/markdown.lua index 2bc6e30..325ec45 100644 --- a/tools/markdown.lua +++ b/tools/markdown.lua @@ -345,4 +345,4 @@ if args[1]~= nil and args[2]~= nil then markdown.saveToFile(output, md) end -return markdown +return markdown \ No newline at end of file