- Updated theme plugin to support changing themes on runtime

- Added 4 themes
- Added Cache System for States
This commit is contained in:
Robert Jelic
2025-11-05 13:56:19 +01:00
parent e4ee937f0f
commit 71d23ee9a2
11 changed files with 304 additions and 46 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -114,4 +114,4 @@ function LayoutManager.destroy(instance)
end
end
return LayoutManager
return LayoutManager

View File

@@ -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")

View File

@@ -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