# XML in Basalt
Basalt provides an XML parser that lets you define your UI layout in a declarative way. This can make your code more readable and maintainable.
## Basic Usage
```lua
local main = basalt.getMainFrame()
-- Load from file
local xmlFile = fs.open("myUI.xml", "r")
main:loadXML(xmlFile.readAll())
xmlFile.close()
-- Or directly as string
main:loadXML([[
]])
```
## Working with Variables
The XML parser uses a scope system to access variables and functions. Any variables you want to use in your XML must be passed in the scope:
```lua
local scope = {
title = "My App",
buttonWidth = 10,
handleClick = function(self)
self:setText("Clicked!")
end
}
main:loadXML([[
]], scope)
```
## State Management
Basalt's state system integrates seamlessly with XML, allowing you to bind element properties to reactive states.
### Inline State Binding
Use the `State:` suffix on any attribute to bind it to a state:
```lua
local scope = {}
main:loadXML([[
]], scope)
```
This automatically calls `setTextState("clicked", "Click me!")` and `setBackgroundState("clicked", "gray")`.
### Nested State Configuration
For more complex state setups, use the `` tag:
```lua
main:loadXML([[
]], scope)
```
## Custom XML Tags
You can register custom tags to create reusable components or handle special logic.
### Registering a Custom Tag
```lua
local XMLParser = basalt.getXMLParser()
XMLParser.registerTagHandler("card", function(node, parent, scope)
-- Create a frame container
local card = parent:addFrame()
card:setBackground(colors.gray)
card:setSize(20, 10)
-- Parse title attribute
if node.attributes.title then
local title = node.attributes.title:gsub("^\"", ""):gsub("\"$", "")
local titleLabel = card:addLabel()
titleLabel:setText(title)
titleLabel:setPosition(2, 1)
end
-- Process children inside the card
if node.children then
for _, child in ipairs(node.children) do
local childTag = child.tag:sub(1,1):upper() .. child.tag:sub(2)
if card["add" .. childTag] then
local element = card["add" .. childTag](card)
element:fromXML(child, scope)
end
end
end
return card
end)
```
### Using Custom Tags
```lua
main:loadXML([[
]])
```
### Advanced Custom Tag Example
Create a reusable dialog component:
```lua
XMLParser.registerTagHandler("dialog", function(node, parent, scope)
local dialog = parent:addFrame()
dialog:setSize(30, 15)
dialog:setBackground(colors.black)
dialog:addBorder({left=true, right=true, top=true, bottom=true})
dialog:center()
-- Title bar
if node.attributes.title then
local title = node.attributes.title:gsub("^\"", ""):gsub("\"$", "")
local titleBar = dialog:addLabel()
titleBar:setText(title)
titleBar:setPosition(2, 1)
titleBar:setForeground(colors.white)
end
-- Close button
local closeBtn = dialog:addButton()
closeBtn:setText("X")
closeBtn:setPosition(28, 1)
closeBtn:setSize(2, 1)
closeBtn:onClick(function()
dialog:remove()
end)
-- Content area (process children)
if node.children then
for _, child in ipairs(node.children) do
local childTag = child.tag:sub(1,1):upper() .. child.tag:sub(2)
if dialog["add" .. childTag] then
local element = dialog["add" .. childTag](dialog)
element:fromXML(child, scope)
end
end
end
return dialog
end)
-- Usage
main:loadXML([[
]])
```
### Unregistering Custom Tags
```lua
XMLParser.unregisterTagHandler("card")
```
### Checking for Custom Tags
```lua
local handler = XMLParser.getTagHandler("card")
if handler then
-- Handler exists
end
```
## Event Handlers
There are two ways to define event handlers:
### 1. Reference a Function in Scope
```lua
local scope = {
btnClick = function(self)
self:setText("Clicked!")
end
}
main:loadXML([[
]], scope)
```
### 2. Using CDATA Sections
```xml
```
## Property Types
The XML parser automatically converts values based on the property type:
```xml