In order to master XUL, you'll need to have a fairly good understanding of its Box Model. This is the system that determines how all elements are laid out visually in a XUL document. It is important to know how it works in order to make interfaces that are easy to localize, skin and use in different types of operating systems, screen sizes and resolutions.
The XUL box model is a significant improvement over the HTML layout model, which is mostly vertical. With XUL you can define vertically oriented as well as horizontally oriented interfaces, providing greater flexibility in interface design.
Any XUL interface can be broken down into the following basic components:
- Alignment and flexibility
- Widths and heights
- Margins and paddings
Menus, toolbar buttons, and even the most complex elements in XUL are composed of these simple ingredients. Complex elements are created from simpler ones through XBL, which will be covered later on. Once you grasp this simple idea, you'll be able to use the DOM Inspector and CSS to mold XUL elements and layouts with great precision and flexibility.
Most of the examples shown in the rest of this section were generated using the XUL Box Alignment Example. We recommend you play around with it for a while to get an idea of how the basic parts of the box model work. In order to use this page, you'll need to install the Remote XUL Manager extension and add developer.mozilla.org to the whitelist.
A XUL box is very similar to an HTML div. It's an invisible rectangular container with no styling at all (divs may have some default styling, though). Their behavior is similar. However, one of the fundamental differences between XUL and HTML has been the fact that XUL boxes can be oriented vertically or horizontally (this behavior is being introduced into HTML standards).
There are 3 basic box elements in XUL: hbox, vbox and box. An hbox is oriented horizontally by default, meaning that its child nodes are displayed next to each other from left to right. A vbox is oriented vertically by default, its child nodes displayed one below the other from top to bottom. A box is a generic version that you can orient as you please, but its default is horizontal orientation, so it's pretty much equivalent to an hbox and it is seldom used.
Here's a very simple example of an hbox with 3 child buttons:
<hbox> <button label="Cat" /> <button label="Parrot" /> <button label="Porcupine" /> </hbox>
This is how it looks on Mac OS (the black border was added for illustrative purposes, boxes don't have borders by default):
If you use a vbox instead, it looks like this:
The orientation of boxes (and most XUL elements) can be controlled using the orient attribute or the -moz-box-orient CSS property. With these you can make an hbox have vertical orientation and viceversa, although that isn't very useful. The CSS property may be useful on some occasions, but using the orient attribute is not recommended because it mixes content and presentation.
An HTML div is as big as its contents unless you override its dimensions using CSS. This is similarly the case for XUL, except there are two flexibility directions to consider. An hbox is as big as its contents horizontally, but it will occupy all the available space vertically. You can make an hbox flexible horizontally with the CSS property -moz-box-flex or the flex attribute. The same applies to a vbox, but in the other direction.
Unlike most style attributes, the flex attribute is considered acceptable to use in XUL code. This is because this attribute is used much too often, and it would require a great deal of CSS code to avoid using it. At any rate, it can always be overriden using CSS, so your extension won't lose skinability because of it.
Flexibility is defined as a numeric value. The default value for most elements is 0, which means that the element will not stretch in the direction of its orientation, and its size in that dimension will be determined by its contents and padding. If you want an element to be flexible, you should set its flexibility to 1. This makes the element stretch to occupy as much available space there is in the direction of its orientation. If we add flexibility to the hbox in our first example, we get the following result:
The box flexes to cover the available horizontal space. The buttons maintain their size.
If we also add flexibility to the "Cat" button, this is what we get:
Now the flexible button is taking the available inner space, moving the other two buttons all the way to the end of the box.
What would happen if we also add flexibility to the "Parrot" button?
Since both buttons have a flexibility of 1, the available space is distributed evenly among the two. Note that this is not always the case. If one of the buttons has a very long label that restricts its minimum size, then it could be the label determining its size, while the other button would flex taking the rest of the room.
Now, if you want a different size distribution in flexible elements, you can use flexibility values higher than 1.
In this case, the "Cat" button has a flex value of 3, while the "Parrot" button still has a flex value of 1. The "Cat" button is now larger in a 3 to 1 proportion. Note that, again, this can depend on the contents of the elements, in this case, the labels and paddings in the buttons. If the label of the "Parrot" button was something much longer, the size ratio would not be kept.
You can have even more control over the size of flexible elements using the minwidth, maxwidth, minheight and maxheight attributes, or their CSS counterparts: min-width, max-width, min-height and max-height. The latter are recommended to keep style code in the skin section of the chrome. As their names should make clear, you can control the flexibility boundaries of elements, thus preventing them from growing or shrinking too much.
Margins and paddings are frequently used in both HTML and XUL to define spacing between elements and inside of elements. The margin determines the space between an element and the elements surrounding it, while the padding determines the space between the borders of a container element and its child nodes, kind of like an inner margin.
Sometimes you also need to define flexible spaces between elements. In this case you should use a spacer element with a flex value. A spacer element is completely invisible and doesn't do more than take space. You should only use it when you need flexible space. If you need fix-sized space it's best to use margins and paddings and avoid adding unnecessary nodes to your XUL document.
XUL, unlike HTML, provides an easy way to align elements both horizontally and vertically. You can either use the align and pack attributes, or the -moz-box-align and -moz-box-pack CSS properties. Packing determines the positioning of the child elements in the direction of the container's orientation. Alignment determines the positioning in the other direction. So, in a flexed hbox, having center alignment and end packing results in this:
One important thing to notice is that aligment and flexibility can't be mixed in some cases. If you add flexibility to one of the buttons, the packing will no longer be useful, but the alignment still makes a difference. It also wouldn't make sense to use flexibility or packing in the hbox if it didn't have any available horizontal space; the container element needs to either be flexible itself, or have a fixed width larger than its contents.
The label element should be used mainly for text in XUL forms, such as the ones you see in the Firefox Options / Preferences window. Here's a typical usage of the label element:
The label says something like "Type a greeting message", and the textbox next to it allows the user to type the message. The control attribute links the label to the textbox. If the user clicks on the label, the textbox will be focused. Also, if the user has a screen reader, the label will be read when the focus is placed on the textbox.
The description element is used for the rest of the cases, where the text is only meant as additional information and is not related to input elements.
Handling text in XUL may seem simple, but it's actually quite a tricky subject. You always have to keep in mind that localized strings may be significantly longer in other languages, specially much longer than English strings. You should avoid using long blocks text, and also avoid designing your UI so that everything fits just right around text. You should always keep in mind that the text can be much longer, and your UI should adapt to it.
Labels should generally be short, and they should always be displayed in a single line. Descriptions may be longer, and in general you should plan it so that they can wrap into multiple lines. In order to have wrapping descriptions, you need to set the text as a child node instead of using the value attribute:
Even then, the text will extend as much as it can in a single line, so you need to add some CSS limits in order to make it wrap inside a XUL dialog or window. You can use the CSS property width to restrict the width of the description so that it wraps as you need it to.
There are other more complicated text wrapping cases where a description won't be good enough. For instance, templates don't allow you to set the internal text in a description element in a way that it wraps properly. One way to work around this is using a textbox element instead. This blog post is a good guide on the subject, and you should keep it in mind if you need to do some advanced text handling.
Another way to handle excessive text length is to use the crop attribute. If the line of text is longer than its container will allow, the text will be cut, showing "..." in the place where it was cut. You can choose where to cut the text, but cropping at the end is the most common practice. Similarly as with wrapping, cropping will only occur is there is no room for the text to grow, so you'll need to restrict the width using CSS.
XUL makes it very easy for you to create a label that is also a link. All you need is to set the text-link class to the label, and add an onclick handler (oncommand won't work for this). The label is displayed with the expected link style so that users can easily recognize it as a link.
The syntax is similar to HTML because it's easier to read this way, but string bundles won't do anything special with it. You'll have to break the string using regular expressions and generate 3 different labels, one with the text-link class. You'll also need to do some CSS work on the labels so that the inner spacing in the text looks right.
Another option is to take it up a notch and really use HTML.
To include HTML in a XUL document, you need to add the namespace for it in the document root:
Then you can have an html:p (paragraph) element where you insert the result of parsing the property string. You can easily parse XML using the DOMParser object.
Use the DOM Inspector extension to look into the Firefox chrome. Select toolbar buttons, menu items, textboxes, etc. and look into the Box Model, CSS Style Rules and Computed Style views. Look into the computed width and height values, and how the paddings and margins affect the dimensions of XUL nodes.
Modify the Hello World extension so that it changes the appearance of Firefox drastically using only CSS. Play with changing the dimensions and appearance of buttons, changing the background color of the toolbars and status bars, font styles, sizes, etc. Note that this is not really what you would expect from an extension, it's more like a theme. However, it's very interesting and useful to see how you can manipulate the appearance of Firefox so easily.
If you're not familiar with CSS, you should read this CSS Getting Started Guide and other online resources before continuing with this tutorial.
This tutorial was kindly donated to Mozilla by Appcoast.