Skip to content

Commit d7a6885

Browse files
author
Sabine Lim
authored
Update xml.md (#86)
1 parent b6114ca commit d7a6885

File tree

1 file changed

+122
-58
lines changed

1 file changed

+122
-58
lines changed

docs/guides/xml.md

+122-58
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ XML (eXtensible Markup Language) is a popular and widely-used markup language fo
22

33
## Loading an XML File in Basalt
44

5-
To load an XML file in Basalt, you'll need to use the `frame:loadLayout` function. This function reads an XML file and returns a table containing the parsed XML data.
5+
To load an XML file in Basalt, you'll need to use the `container:loadLayout` function. This function reads an XML file and runs any scripts or adds any objects within.
66

77
Here's an example of how to load an XML file:
88

99
```lua
1010
local basalt = require("basalt")
1111

12-
local main = basalt.createFrame():loadLayout("path/to/your/layout.xml")
12+
basalt.createFrame():loadLayout("path/to/your/layout.xml")
1313
```
1414

1515
Make sure that the specified XML file is accessible and located within your project's file system.
@@ -21,108 +21,172 @@ Basalt uses XML to define UI elements and their properties. By using XML, you ca
2121
Here's an example of an XML file that defines a simple UI layout for Basalt:
2222

2323
```xml
24-
<label id="titleLabel" text="Welcome to Basalt!" x="10" y="2" />
25-
<button id="okButton" text="OK" x="10" y="5" />
24+
<label text="Welcome to Basalt!" x="10" y="2" />
25+
<button text="OK" x="10" y="5" />
2626
```
2727

2828
In this example, we define a `Label` and a `Button` with their respective properties, such as `id`, `text`, `x`, and `y`.
2929

30-
To use the loaded XML data to create the UI elements in Basalt, you can use `main:getXMLElements()`:
30+
## Props
31+
32+
Properties, or props for short, allow the layout or object to take parameters as input which can be used in the layout. There are 2 types of props: hard-coded string props and computed props.
33+
34+
Hard-coded string props use the `prop="some text"` syntax. The above example demonstrated this, but by using the computed `prop={some Lua expression}` syntax, these props can be used in a much more powerful way. Anything between the curly braces is evaluated as a Lua expression. For example:
35+
36+
```xml
37+
<label text={"Some text concatenated with a number: " .. 420 * 7 + 9 } />
38+
```
39+
40+
You can pass props to a layout in the form of a table to the `loadLayout` function:
3141

3242
```lua
3343
local basalt = require("basalt")
3444

35-
local main = basalt.createFrame():loadLayout("path/to/your/layout.xml")
36-
37-
local uiElements = main:getXMLElements()
45+
basalt.createFrame():loadLayout("path/to/your/layout.xml", { buttonText = "Click me!", onClick = function() basalt.log("Testing") end })
46+
```
3847

39-
local titleLabel = uiElements["titleLabel"]
48+
Props can then be consumed within the layout in the form of the `props` global variable, accessible anywhere within the layout including within computed props.
4049

41-
titleLabel:setText("New Title")
50+
```xml
51+
<button text={props.buttonText} onClick={props.onClick} />
4252
```
4353

44-
## Using Lua Code in XML
54+
## Scripts
4555

46-
In addition to defining UI elements, you can also include Lua code within your XML files for Basalt. This allows you to perform more complex operations or customize your UI elements based on conditions or data.
56+
In addition to defining UI elements, you can also include Lua code within your XML files for Basalt. This allows you to perform more complex operations.
4757

48-
To include Lua code in your XML file, you can use the `<script>` tag. Any Lua code enclosed within the `<script>` tag will be executed by Basalt when parsing the XML data.
58+
To include Lua code in your XML file, you can use the `<script>` tag. Any Lua code enclosed within the `<script>` tag will be executed by Basalt when parsing the XML data. This script shares the global scope with the computed props.
4959

5060
Here's an example of how to include Lua code in your XML file:
5161

5262
```xml
53-
<label id="titleLabel" text="Welcome to Basalt!" x="10" y="2" />
54-
<button id="okButton" text="OK" x="10" y="5" />
5563
<script>
56-
-- Lua code to change the text of the titleLabel based on a condition
57-
if someCondition then
58-
titleLabel:setText("Condition Met!")
64+
if (props.isLoggedIn) then
65+
labelText = "Logged in!"
66+
else
67+
labelText = "Logged out!"
5968
end
6069
</script>
70+
71+
<label text={labelText} />
6172
```
6273

63-
To share variables or data between multiple `<script>` tags in your XML file, you can use the global shared table provided by Basalt. This table allows you to store values that can be accessed across different `<script>` tags in your XML file.
74+
## Nested layouts
6475

65-
Here's an example of using the shared table to share data between two `<script>` tags:
76+
Sometimes a UI gets so complex that it becomes desirable to split it up into several sub-layouts. For this, you can import layouts within a layout:
6677

6778
```xml
68-
<script>
69-
-- Store a value in the shared table
70-
shared.myValue = 42
71-
</script>
72-
<label id="titleLabel" text="Welcome to Basalt!" x="10" y="2" />
73-
<button id="okButton" text="OK" x="10" y="5" />
74-
<script>
75-
-- Access the stored value from the shared table
76-
local myValue = shared.myValue
77-
78-
-- Perform an operation using the shared value, e.g., update titleLabel's text
79-
titleLabel:setText("Shared Value: " .. myValue)
80-
</script>
79+
<script>
80+
local basalt = require("basalt")
81+
AnotherLayout = basalt.layout("path/to/another/layout.xml")
82+
</script>
83+
84+
<label text="Nested layouts are fun" />
85+
<AnotherLayout someProp="Hello" anotherProp="World" aComputedProp={function() return "Basalt rules" end} />
8186
```
8287

83-
In this example, we first store a value in the shared table in one `<script>` tag, and then access that value in another `<script>` tag to update the titleLabel's text.
88+
This layout can be passed props like any other object in the layout, as seen in the example above.
89+
90+
## Reactivity (BETA)
91+
92+
Reacting to user input is easier than ever with Basalt XML's concept of reactive values and observers for said values. This powerful feature allows for properties to be derived automatically from reactive values, without needing the programmer to manually call functions to update the object.
8493

85-
You can also include Lua code directly within event properties of UI elements. This allows you to execute specific actions or manipulate UI elements when certain events occur.
94+
To create a reactive value, simply use the `basalt.reactive(initialValue)` function, which returns getter and setter functions. For example:
95+
96+
```xml
97+
<script>
98+
local basalt = require("basalt")
99+
getTimesClicked, setTimesClicked = basalt.reactive(0)
100+
</script>
101+
```
86102

87-
To include Lua code in an event property, simply add the Lua code within the event property's value in the XML tag. The Lua code will be executed when the specified event is triggered.
103+
You could then hook up this reactive value to a property.
88104

89105
```xml
90-
<label id="titleLabel" text="Welcome to Basalt!" x="10" y="2" />
91-
<button id="okButton" text="OK" x="10" y="5" onClick="titleLabel:setText('Button clicked!')" />
106+
<script>
107+
local basalt = require("basalt")
108+
getTimesClicked, setTimesClicked = basalt.reactive(0)
109+
</script>
110+
111+
<button text={"Times clicked: " .. getTimesClicked()} />
92112
```
93113

94-
or
114+
This subscribes the button text to the value of times clicked. If this value is updated via the setter function, the button text will automatically update as well. So let's add an onClick event to do this!
95115

96116
```xml
97-
<label id="titleLabel" text="Welcome to Basalt!" x="10" y="2" />
98-
<button id="okButton" text="OK" x="10" y="5">
99-
<onClick>
100-
titleLabel:setText('Button clicked!')
101-
</onClick>
102-
</button>
117+
<script>
118+
local basalt = require("basalt")
119+
getTimesClicked, setTimesClicked = basalt.reactive(0)
120+
onClick = function()
121+
setTimesClicked(getTimesClicked() + 1)
122+
end
123+
</script>
124+
125+
<button text={"Times clicked: " .. getTimesClicked()} onClick={onClick} />
103126
```
104127

105-
In both examples, you can see that XML provides a straightforward way to build a Basalt user interface. The XML format allows you to define the UI structure and easily set properties for each UI element, such as position, size, and text.
128+
Voila. You now have a button that displays the number of times it's been clicked.
129+
130+
# Effects
131+
132+
In addition to reactive values, there are effects that are triggered by them. You can think about it like this: reactive values produce updates, while effects detect them and do something in response.
133+
134+
Effects are created using the `basalt.effect(function)` function. Any reactive values that are accessed during the effect's execution are automatically subscribed to, and the effect will re-run in response to updates to these values.
135+
136+
For example, you could create an effect that writes a message to the logs whenever the times clicked updates:
137+
138+
```xml
139+
<script>
140+
local basalt = require("basalt")
141+
getTimesClicked, setTimesClicked = basalt.reactive(0)
142+
onClick = function()
143+
setTimesClicked(getTimesClicked() + 1)
144+
end
145+
basalt.effect(function()
146+
basalt.log("Button clicked. New times clicked = " .. getTimesClicked())
147+
end)
148+
</script>
149+
150+
<button text={"Times clicked: " .. getTimesClicked()} onClick={onClick} />
151+
```
106152

107-
Notably, you can access UI elements by their assigned ID directly in the event code. In the examples above, the titleLabel and okButton elements are accessed simply by referencing their IDs. This convenient feature eliminates the need to search for or store references to the elements in your code.
153+
In fact, props are internally implemented using effects! Effects that set the corresponding property in the object to the new reactive value.
108154

109-
Remember: IDs have to be unique!
155+
# Derived values
110156

111-
## Reactive properties (BETA)
157+
If reactive values are the source of truth, derived values can be thought of as a dependency that uses them and transforms them. Similarly to effects, they also update whenever the reactive values they observe update.
112158

113-
Most properties can also be set to track a shared variable using the curly braces {} syntax. In this case, the initial value for the variable should be set inside the `<script>` tag. When this variable is modified, the rendered value will be automatically updated.
159+
To create a derived value, use the `basalt.derived(function)` function, which returns a getter function for the value. Any effect observing this derived value will update if the derived value updates, which itself updates in response to a source reactive value updating, in a chain reaction.
114160

115-
The earlier example rewritten using reactive properties:
161+
The above button example could be rewritten as:
116162

117163
```xml
118-
<label id="titleLabel" text="Welcome to Basalt!" x="10" y="2" />
119-
<button id="okButton" text={shared.okButtonText} x="10" y="5">
120-
<onClick>
121-
shared.okButtonText = "Button clicked!"
122-
</onClick>
123-
</button>
164+
<script>
165+
local basalt = require("basalt")
166+
getTimesClicked, setTimesClicked = basalt.reactive(0)
167+
onClick = function()
168+
setTimesClicked(getTimesClicked() + 1)
169+
end
170+
getButtonText = basalt.derived(function()
171+
return "Times clicked: " .. getTimesClicked()
172+
end)
173+
</script>
174+
175+
<button text={getButtonText()} onClick={onClick} />
176+
```
177+
178+
# Untracked reactive value access
179+
180+
Sometimes you might want to use a reactive value in an unreactive way. Perhaps you would like a property to be computed based on it only once, never updating afterwards. This can be accomplished using the `basalt.untracked(function)` function:
124181

182+
```xml
125183
<script>
126-
shared.okButtonText = "OK"
184+
basalt = require("basalt")
185+
getTimesClicked, setTimesClicked = basalt.reactive(0)
186+
onClick = function()
187+
setTimesClicked(getTimesClicked() + 1)
188+
end
127189
</script>
190+
191+
<button text={"This value should never update: " .. getTimesClicked()} onClick={onClick}/>
128192
```

0 commit comments

Comments
 (0)