Ever since Version 3 of the Wolfram Language, there has been rich support for arbitrary mathematical typesetting and layout. Underlying all that power was a so-called box language, which allowed notebooks themselves to be Wolfram Language expressions. This approach turned out to be very powerful, and has formed the basis of many unique features in the Wolfram Language. However, despite the power of the box language, in practice it was awkward enough for users to access directly that few did.
Starting in Version 6, there is a higher-level interface to this box language which takes much of the pain out of using boxes directly, while still exposing all the same typesetting and layout power. Functions in this new layer are often referred to as box generators, but there is no need for you to be aware of the box language to use them effectively. In this tutorial, we will take a look at box generators that are relevant for displaying a wide variety of expressions, and we will show some ways in which they can be used to generate beautifully formatted output that goes beyond simple mathematical typesetting.
The Wolfram System front end supports all the usual style mechanisms available in word processors, for example including menus for changing font characteristics. However, it used to be very difficult to access those styling mechanisms automatically in generated output. Output continued to be almost universally plain 12 point Courier (or Times for those people using TraditionalForm). To address this, the function Style was created. Whenever you evaluate a Style expression, its output will be displayed with the given style attributes active.
There are hundreds of formatting options that you could apply with Style—see the documentation for Style for a more complete listing—but there are a handful that are by far the most common, listed here.
|Format ▶ Size ▶ 14||FontSize->14||14|
|Format ▶ Text Color ▶ Gray||FontColor->Gray||Gray|
|Format ▶ Face ▶ Bold||FontWeight->Bold||Bold|
|Format ▶ Face ▶ Italic||FontSlant->Italic||Italic|
|Format ▶ Background Color ▶ Yellow||Background->Yellow|
|Format ▶ Font||FontFamily->"Times"|
|Format ▶ Style ▶ Subsection||"Subsection"|
Another common thing to want is to have a portion of the output styled like text. It can look quite strange to have text appear in a font which is intended for use by code. For that purpose, we have a function Text which ensures that its argument will always be rendered in a text font. (Those of you familiar with Wolfram Language graphics will recognize the Text function as a graphics primitive, but that use does not conflict with this use outside of graphics.)
Style can be used to set up a region on the screen where any option is active, not just options related to fonts. Later in this tutorial, we will see how Style can even affect the display characteristics of other formatting constructs, like Grid or Tooltip.
Using two-dimensional layout structures can be just as useful as applying style directives to those structures. In the Wolfram Language, the primary function for such layout is Grid. Grid has very flexible layout features, including the ability to arbitrarily adjust things like alignment, frame elements, and spanning elements. (Other tutorials go into Grid features in greater detail, but we will cover the highlights here.)
Look again at the Style example which displays prime and composite numbers differently.
To put this into a Grid, we first use Partition to turn this 100-element list into a 10×10 array. Although you can give Grid a ragged array (a list whose elements are lists of different lengths), in this case we give Grid a regular array, and the resulting display is a nicely formatted layout.
Notice that the columns are aligned on center, and there are no frame lines. It is an easy matter to change either of these using Grid options.
A complete description of all Grid options and their syntax is beyond the scope of this document, but it is possible to do some remarkable things with them. See the complete Grid documentation for complete details.
There are a few convenience constructs related to Grid. One is Column, which takes a flat list of elements and arranges them vertically. This would be slightly awkward to do with Grid. Here is a simple example, viewing the options of column in, well, a column.
What about laying out a list of things horizontally? In that case, the main question you need to ask is whether you want the resulting display to line wrap like a line of math or text would, or whether you want the elements to remain on a single line. In the latter case, you would use Grid applied to a 1×n array.
But notice in this example that the overall grid shrinks so that it fits in the available window width. As a result, there are elements of the grid which themselves wrap onto multiple lines. This is due to the default ItemSize option of Grid. If you want to allow the elements of a grid to be as wide as they would naturally be, set ItemSize to Full.
Of course, now the whole grid is too wide to fit on one line (unless you make this window very wide), and so there are elements in the grid which you cannot see. That brings us to the other horizontal layout function: Row.
Given a list of elements, Row will allow the overall result to word wrap in the natural way, just like a line of text or math would. This type of layout will be familiar to those of you who might have used the old (and now obsolete) SequenceForm function.
As you can see, Row does not leave space between elements by default. But if you give a second argument, that expression is inserted between elements. Here we use a comma, but any expression can be used.
Using Output as Input
This is a good time to point out that Style, Grid, and all other box generators are persistent in output. If you were to take a piece of output that had some formatting created by Style or Grid and reuse that as input, the literal Style or Grid expressions would appear in the input expression. Those of you familiar with the old uses of StyleBox and even functions like MatrixForm will find this a change.
Consider taking the output of this Grid command, which has lots of embedded styles, and using it in some input expression.
Notice that the grid is still a grid, it is still blue, and the elements are still bold or gray as before. Also notice that having literal Grid and Style in the expression interferes with what would have otherwise been adding a scalar to a matrix, and raising the result to a power. This distinction is very important, since you almost always want these composite structures to resist being interpreted automatically in some way. However, if you ever do want to get rid of these wrappers and get at your data, that is easy enough to do.
Special Grid Entries
To allow more flexible two-dimensional layout, Grid accepts a few special symbols like SpanFromLeft as entries. The entry SpanFromLeft indicates that the grid entry immediately to the left should take up its own space and also the space of the spanning character. There are also SpanFromAbove and SpanFromBoth. See "Grids, Rows, and Columns" for detailed information.
This approach can be used to create complicated spanning setups. Typing something like the following as an input would take a long time. Luckily you can create this table interactively by using Make Spanning and Split Spanning in the Insert ▶ Table/Matrix submenu. If you want to see what would be involved in typing this, evaluate the cell, which will show how it should be typed as input.
We have already seen how to apply things like alignment and background to a grid as a whole, or to individual columns or rows. What we have not seen though is how to override that for an individual element. Say you want your whole grid to have the same background, except for a few special elements. A convenient way to do that is to wrap each such element in Item, and then specify options to Item which override the corresponding option in Grid.
You could override this option with Style too, but the purpose of Item is to override it in a way that knows about the two-dimensional layout of Grid. Notice in the preceding output that whenever two of the yellow cells are next to each other, there is no blue space between them. That would be impossible to do with constructs other than Item.
The same thing goes for all Item options, not just Background. Consider the Frame option. If you want no frame elements except around certain specified elements, you might think that you have to wrap them in their own Grid with the Frame->True setting. (We will learn a much easier way to add a frame around an arbitrary expression in the next section.)
But notice that adjacent framed elements do not share their boundaries. Compare that with using Item, below, which has enough information to not draw more frame elements than are necessary. Notice now the frames of 2 and 11 meet at a single point, and how the frames of 2 and 3 share a single-pixel line, which in turn is perfectly aligned with the left frame of 13 and 23. That is the power of Item.
Adding a frame or a label to an expression can be done with Grid, but conceptually these are much simpler operations than general two-dimensional layout, and so there are correspondingly simpler ways to get them. For instance, Framed is a simple function for drawing a frame around an arbitrary expression. This can be useful to draw attention to parts of an expression, for instance.
Labeled is another such function, which allows labels to be placed at arbitrary locations around a given expression. Here we add a legend to the Grid example from the last section. (Spacer is just a function that is designed to leave empty space.)
Panel is yet another framing construct, which uses the underlying operating system's panel frame. This is different from Frame, as different operating systems might use a drop shadow, rounded corners, or fancier graphic design elements for a panel frame.
Note that Panel has its own concept of font family and size as well, so the contents of Grid change font family and size, and the Text changes font size. (Text has its own opinion about font family though, and so it remains in the Wolfram System's text font.) We will talk about this in some detail below in the section on the BaseStyle option.
Finally, we should point out that Panel itself has an optional second argument to specify one or more labels, which are automatically positioned outside the panel, and an optional third argument to give details of that position. See the documentation for Panel for more detail.
The annotations mentioned so far have a very definite visual component. There are a number of annotations which are effectively invisible, until the user needs them. Tooltip for example does not change the display of its first argument, and only when you move the mouse pointer over that display is the second argument shown, as a tooltip.
Mouseover is another such function, but instead of displaying the result in a tooltip, it uses the same area of the screen that had been used for the display before you moved the mouse pointer over it. If the two displays are different sizes, then the effect can be jarring, so it is a good idea to use displays which are closer to the same size, or use the Mouseover ImageSize option to leave space for the larger of the two displays, regardless of which is being displayed.
Also similar to Tooltip are StatusArea and PopupWindow. StatusArea displays the extra information in the notebook's status area, typically in the lower-left corner, while PopupWindow will display extra information in a new window when clicked.
When using annotations that are triggered merely by moving the mouse pointer over a region of the screen, it is important to keep the user in mind. Moving the mouse is not something that should trigger a long evaluation or a lot of visual clutter. But used sparingly, annotations can be quite helpful to users.
Finally, note that all these annotations work perfectly well in graphics too. So you can provide tooltips or mouseovers to aid users in understanding a complicated graphic you have created. In fact, even visualization functions like ListPlot or DensityPlot support Tooltip. See the documentation for details.
As we saw in the section "Frames and Labels", constructs like Panel actually work much like Style, in that they set up an environment in which a set of default styles is applied to their contents. This can be overridden by explicit Style commands, but it can also be overridden for the Panel itself, through the BaseStyle option. BaseStyle can be set to a style or a list of style directives, just like you would use in Style. And those directives then become the ambient default within the scope of that Panel.
Actually, almost all of these box generators have a BaseStyle option. For instance, here is a grid in which the default font color is blue. Notice that the elements that were gray stay gray, since the inner Style wrapper trumps the outer Grid BaseStyle. (This is one of the principal characteristics of option inheritance, which is beyond the scope of this document to discuss.)
Say you have an expression with multiple occurrences of the same box generator, like a Framed or a Panel, and you want to change all of them to have the same set of options. It might be cumbersome to go through and add the same set of options to every occurrence of that function. Thankfully, there is an easier way.
DefaultOptions is an option to Style which, when set to a list of elements of the form . sets up an environment with the given options as the ambient default for the given box-generating head. Those options will be active throughout the Style wrapper, but only in any instances of the associated box generator.
Suppose you had an expression that contained some Framed items, and you wanted them all to be drawn with the same background and frame style.
Actually, that input is too short to see the advantage of this syntax. Say you had this same list, but specified manually.
Now inserting Background and FrameStyle options into every Framed wrapper is prohibitively time consuming, although you certainly could do it (or you could write a program to do it for you). But using DefaultOptions, you can effectively set up an environment in which all Framed wrappers will use your settings for Background and FrameStyle, thus.
This approach makes it easy to create structures that follow uniform style guidelines without having to specify those styles in more than one place, which makes for considerably cleaner code, smaller file sizes, and easier maintenance.
No discussion of formatted output would be complete without at least a nod toward the formatting constructs that are unique to mathematical syntaxes.
We will not discuss these at length, but we will point out that these constructs do not have any built-in mathematical meaning in the kernel. For example, Superscript[a,b] will not be interpreted as Power[a,b], even though their displays are identical. So you can use these as structural elements in your formatted output without having to worry about their meaning affecting your display.
Using the Box Language
One final note. Those of you who are already familiar with the box language might occasionally find that these box generators get in the way of your constructing low level boxes yourselves, and inserting their display into a piece of output. That can be true for any layered technology where one abstraction layer attempts to hide the layers on which it sits. However, there is a simple loophole through which you can take boxes which you happen to know are valid, and display them directly in output: RawBoxes.
As with all loopholes, RawBoxes gives you added flexibility, but it also allows you to shoot yourself in the foot. Use with care. And if you are not yet familiar with the box language, perhaps you should not use it at all.