As your interfaces grow, you'll find yourself writing the same tag over and over with the same fields: every heading with the same size and colour, every card with the same border and layout. The class tag lets you define a new kind of tag of your own, and then use it as often as you like, just like a built-in tag.
<important:class *=<span bold=true color={orange}>>
This is <important {really}> worth knowing.</n>
And so is <important {this}>.
This is really worth knowing.The first line defines a new tag type called important. After that, <important ...>
works like any other tag.
The * field holds the definition: a single tag that each new important tag is made from. You saw the same * field in the chapter on arrays, and it works the same way. Here, the definition is a bold, orange span. Anything you put in an important tag's primary field goes into the span's primary field.
The name you give a class is the name of the new tag type. Like any other name, it can be used anywhere in the scope where you created it.
A definition can be as complicated as you like, as long as it's a single tag on the outside. Here's a class for the profile card from the chapter on making things look good:
<card:class _=name job=job *=
<box width=16,em border=2 borderColor=x2E8B57 margin=1,em spacing=0.5,em align={center} {
<name:span size=18,pt bold=true color=x2E8B57 {}></n>
<job:span italic=true {}>
}>
>
<card job={Mathematician} {Ada Lovelace}>
<card job={Computer scientist} {Grace Hopper}>
<card job={Mathematician} {Alan Turing}>
Three cards, each with three lines of code instead of eight. And if you decide the cards should be blue, you only have to change the definition.
This class has two new fields of its own, which need explaining.
The names inside a class definition, like name and job above, are hidden. Someone using a card can't reach inside it and see them. That's deliberate: it means the inside of a class can change without breaking the code that uses it.
To let users of your class get at something inside it, add a field to the class tag. The field's name is the name users will see, and its value is the path to the thing inside the definition that it stands for:
| Class field | Means |
|---|---|
job=job | the card has a job field, which is the inner span called job |
jobColour=job.color | a jobColour field, which is the color field of that span |
bg=backgroundColor | a bg field, which is the backgroundColor field of the outer box |
The special _ field chooses what the class's primary field stands for. In the card,
_=name means the text in a card's primary field goes into the name span. Without
_, the primary field is the primary field of the outer tag in the definition, as it was for
important.
Once a field is exposed, it works like any other field, both when creating the tag and afterwards:
<ada:card job={Mathematician} {Ada Lovelace}>
<ada.job {Mathematician and writer}>
<putLn ada> <! sends "Ada Lovelace" !>
Anything you don't expose stays hidden, even from code that reaches the card through a reference pointer.
A definition can include code fields too, so every tag made from the class also behaves the same way. Here's the "picking a box" example from the chapter on reference pointers again, with the repeated code moved into a class:
print('''
<selected:scalar null>
<choice:class _=label bg=backgroundColor fg=color *=
<self:box width=100 height=60 border=1 backgroundColor=null color=null
onClick=
<if selected then=<selected.*.bg null>, <selected.*.fg null>>,
<selected self.&>,
<selected.*.bg {gold}>,
<selected.*.fg {black}>,
<putLn label>
{<label:span {}>}
>
>
<choice {A}> <choice {B}> <choice {C}> <choice {D}>
''')
while True:
print('You picked ' + input() + '.</n>')
ABCDAdding a fifth box now takes a few characters, rather than another ten lines. Some things to notice:
self.& is a reference to the box that was
clicked.A class definition can be any presentation tag, presentation container tag or persistent logic tag. It can't be a transient logic tag, since those don't leave anything behind to be an instance of the class.