diff --git a/src/elements/BaseElement.lua b/src/elements/BaseElement.lua index 47bc637..be01d87 100644 --- a/src/elements/BaseElement.lua +++ b/src/elements/BaseElement.lua @@ -101,6 +101,7 @@ function BaseElement:init(props, basalt) self.basalt = basalt self._registeredEvents = {} self._registeredStates = {} + self._cachedActiveStates = nil local currentClass = getmetatable(self).__index @@ -142,6 +143,7 @@ function BaseElement:postInit() return self end self._postInitialized = true + self._modifiedProperties = {} if(self._props)then for k,v in pairs(self._props)do self.set(k, v) @@ -234,6 +236,7 @@ function BaseElement:setState(stateName, priority) states[stateName] = priority or 0 self.set("states", states) + self._cachedActiveStates = nil return self end @@ -246,6 +249,7 @@ function BaseElement:unsetState(stateName) if states[stateName] ~= nil then states[stateName] = nil self.set("states", states) + self._cachedActiveStates = nil end return self end @@ -282,6 +286,11 @@ end --- @shortDescription Gets all active states --- @return table states Array of {name, priority} sorted by priority function BaseElement:getActiveStates() + -- Return cached version if available + if self._cachedActiveStates then + return self._cachedActiveStates + end + local states = self.get("states") local result = {} @@ -291,6 +300,7 @@ function BaseElement:getActiveStates() table.sort(result, function(a, b) return a.priority > b.priority end) + self._cachedActiveStates = result return result end diff --git a/src/elements/Frame.lua b/src/elements/Frame.lua index 0dedd21..6201e37 100644 --- a/src/elements/Frame.lua +++ b/src/elements/Frame.lua @@ -29,7 +29,6 @@ function Frame.new() self.class = Frame self.set("width", 12) self.set("height", 6) - self.set("background", colors.gray) self.set("z", 10) return self end diff --git a/src/elements/Label.lua b/src/elements/Label.lua index 164bd0b..e8d5ff3 100644 --- a/src/elements/Label.lua +++ b/src/elements/Label.lua @@ -37,7 +37,6 @@ function Label.new() local self = setmetatable({}, Label):__init() self.class = Label self.set("z", 3) - self.set("foreground", colors.black) self.set("backgroundEnabled", false) return self end @@ -49,10 +48,6 @@ end --- @protected function Label:init(props, basalt) VisualElement.init(self, props, basalt) - if(self.parent)then - self.set("background", self.parent.get("background")) - self.set("foreground", self.parent.get("foreground")) - end self.set("type", "Label") return self end diff --git a/src/elements/List.lua b/src/elements/List.lua index c1c9614..b4570a2 100644 --- a/src/elements/List.lua +++ b/src/elements/List.lua @@ -71,7 +71,6 @@ function List.new() self.set("width", 16) self.set("height", 8) self.set("z", 5) - self.set("background", colors.gray) return self end diff --git a/src/layoutManager.lua b/src/layoutManager.lua index 34ba231..79197d1 100644 --- a/src/layoutManager.lua +++ b/src/layoutManager.lua @@ -114,4 +114,4 @@ function LayoutManager.destroy(instance) end end -return LayoutManager +return LayoutManager \ No newline at end of file diff --git a/src/plugins/theme.lua b/src/plugins/theme.lua index 020d062..5c1e9df 100644 --- a/src/plugins/theme.lua +++ b/src/plugins/theme.lua @@ -4,25 +4,49 @@ local errorManager = require("errorManager") local defaultTheme = { default = { - background = colors.lightGray, + background = colors.cyan, foreground = colors.black, }, BaseFrame = { background = colors.white, foreground = colors.black, - Frame = { + Container = { + default = { + background = colors.cyan, + foreground = colors.black, + }, background = colors.black, - names = { - basaltDebugLogClose = { - background = colors.blue, - foreground = colors.white + Button = { + background = colors.cyan, + foreground = colors.black, + states = { + clicked = { + background = colors.white, + foreground = colors.black, + } } }, + Input = { + background = colors.cyan, + foreground = colors.black, + }, + Label = { + foreground = colors.white, + }, }, Button = { background = colors.cyan, foreground = colors.black, + states = { + clicked = { + background = colors.black, + foreground = colors.cyan, + } + } + }, + Label = { + foreground = colors.black, }, names = { @@ -96,26 +120,6 @@ local function lookUpTemplate(theme, path) 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 @@ -139,17 +143,46 @@ 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 theme.default then + for k,v in pairs(theme.default) do if type(v) ~= "table" then result[k] = v end end end + local current = theme + for i = 1, #path do + local types = path[i] + local found = false - if next(result) == nil then - result = getDefaultProperties(theme, elementType) + for _, elementType in ipairs(types) do + if current[elementType] then + current = current[elementType] + found = true + if current.default then + for k,v in pairs(current.default) do + if type(v) ~= "table" then + result[k] = v + end + end + end + break + end + end + + if not found then + current = nil + break + end + end + + local themeTable = lookUpTemplate(theme, path) + if themeTable then + for k,v in pairs(themeTable) do + if type(v) ~= "table" or k == "states" then + result[k] = v + end + end end applyNamedStyles(result, theme, elementType, elementName, themeTable) @@ -163,22 +196,53 @@ end --- @param applyToChildren boolean? Whether to apply theme to child elements (default: true) --- @return BaseElement self The element instance function BaseElement:applyTheme(applyToChildren) + local backup = {} + if self._modifiedProperties then + for prop, _ in pairs(self._modifiedProperties) do + backup[prop] = true + end + end + local styles = self:getTheme() if(styles ~= nil) then for prop, value in pairs(styles) do - local config = self._properties[prop] - if(config)then - if((config.type)=="color")then - if(type(value)=="string")then - if(colors[value])then - value = colors[value] + if prop ~= "states" and not backup[prop] then + local config = self._properties[prop] + if(config)then + if((config.type)=="color")then + if(type(value)=="string")then + if(colors[value])then + value = colors[value] + end + end + end + self.set(prop, value) + end + end + end + if styles.states then + for stateName, stateConfig in pairs(styles.states) do + for prop, value in pairs(stateConfig) do + if prop ~= "priority" then + local config = self._properties[prop] + local capitalizedName = prop:sub(1,1):upper() .. prop:sub(2) + if(config)then + if((config.type)=="color")then + if(type(value)=="string")then + if(colors[value])then + value = colors[value] + end + end + end + self["set" .. capitalizedName .. "State"](self, stateName, value) end end end - self.set(prop, value) end end end + self._modifiedProperties = backup + if(applyToChildren~=false)then if(self:isType("Container"))then local children = self.get("children") diff --git a/src/propertySystem.lua b/src/propertySystem.lua index 9f7bf34..04bf87f 100644 --- a/src/propertySystem.lua +++ b/src/propertySystem.lua @@ -263,6 +263,7 @@ function PropertySystem:__init() self._values = {} self._observers = {} self._states = {} + self._modifiedProperties = {} self.set = function(name, value, ...) local oldValue = self._values[name] @@ -275,6 +276,7 @@ function PropertySystem:__init() self:updateRender() end self._values[name] = applyHooks(self, name, value, config) + self._modifiedProperties[name] = true if oldValue ~= value and self._observers[name] then for _, callback in ipairs(self._observers[name]) do callback(self, value, oldValue) @@ -431,6 +433,7 @@ function PropertySystem:_updateProperty(name, value) oldValue = oldValue(self) end + self._modifiedProperties[name] = true self._values[name] = value local newValue = type(value) == "function" and value(self) or value diff --git a/themes/classic.json b/themes/classic.json new file mode 100644 index 0000000..4f954d6 --- /dev/null +++ b/themes/classic.json @@ -0,0 +1,47 @@ +{ + "theme": "classic", + "default": { + "background": "black", + "foreground": "lightGray" + }, + "BaseFrame": { + "background": "lightGray", + "Container": { + "background": "gray", + "foreground": "white", + "Button" : { + "background" : "black", + "foreground" : "lightGray", + "states": { + "clicked": { + "background": "white", + "foreground": "black" + } + } + }, + "Input": { + "background": "black", + "foreground": "lightGray" + } + }, + "Button": { + "background": "black", + "foreground": "lightGray", + "states": { + "clicked": { + "background": "white", + "foreground": "black" + } + } + }, + "Label": { + "foreground": "black" + }, + "names": { + "basaltDebugLog": { + "background": "black", + "foreground": "white" + } + } + } +} \ No newline at end of file diff --git a/themes/dark.json b/themes/dark.json new file mode 100644 index 0000000..8f47103 --- /dev/null +++ b/themes/dark.json @@ -0,0 +1,44 @@ +{ + "theme": "dark", + "default": { + "background": "white", + "foreground": "black" + }, + "BaseFrame": { + "background": "black", + "Container": { + "background": "gray", + "foreground": "white", + "Button": { + "background": "lightGray", + "foreground": "black", + "states": { + "clicked": { + "background": "blue", + "foreground": "white" + } + } + }, + "Input": { + "background": "black", + "foreground": "lightGray" + } + }, + "Button": { + "background": "gray", + "foreground": "white", + "states": { + "clicked": { + "background": "blue", + "foreground": "white" + } + } + }, + "names": { + "basaltDebugLog": { + "background": "red", + "foreground": "white" + } + } + } +} diff --git a/themes/light.json b/themes/light.json new file mode 100644 index 0000000..21143dd --- /dev/null +++ b/themes/light.json @@ -0,0 +1,50 @@ +{ + "theme": "light", + "default": { + "background": "black", + "foreground": "white" + }, + "BaseFrame": { + "background": "lightGray", + "Container": { + "background": "white", + "foreground": "black", + "Button": { + "background": "black", + "foreground": "white", + "states": { + "clicked": { + "background": "lightGray", + "foreground": "black" + } + } + }, + "Input": { + "background": "black", + "foreground": "white" + }, + "Label": { + "foreground": "black" + } + }, + "Button": { + "background": "black", + "foreground": "white", + "states": { + "clicked": { + "background": "white", + "foreground": "black" + } + } + }, + "Label": { + "foreground": "black" + }, + "names": { + "basaltDebugLog": { + "background": "orange", + "foreground": "white" + } + } + } +} diff --git a/themes/orange.json b/themes/orange.json new file mode 100644 index 0000000..999343f --- /dev/null +++ b/themes/orange.json @@ -0,0 +1,47 @@ +{ + "theme": "orange", + "default": { + "background": "black", + "foreground": "orange" + }, + "BaseFrame": { + "background": "white", + "Container": { + "default": { + "background": "black", + "foreground": "orange" + }, + "background": "orange", + "foreground": "white", + "Button": { + "background": "black", + "foreground": "orange", + "states": { + "clicked": { + "background": "white", + "foreground": "orange" + } + } + }, + "Label": { + "foreground": "black" + } + }, + "Button": { + "background": "black", + "foreground": "orange", + "states": { + "clicked": { + "background": "orange", + "foreground": "white" + } + } + }, + "names": { + "basaltDebugLog": { + "background": "red", + "foreground": "white" + } + } + } +}