In the previous section we...

using Term  # hidden
tprint("{green}...have seen how to add some {gold3 bold underline}style{/gold3 bold underline} to our text")  # hidden
...have seen how to add some style to our text

and that's great, but it's not enough. If you want to create really beautiful and structured terminal outputs, a bit of color and bold text is not enough. You want to be able to create panels to separate different pieces of content, lines to mark out different sections, you want to be able to control the aspect (e.g. line length) of the content you are printing and, most importantly, you want to do all this without too many headaches. Term.jl has got your back.

In this section we will look at Renderable objects (subtypes of AbstractRenderable) such as TextBox and Panel. Each type of renderable also has a dedicated pages under the section "Renderables" where the renderable is described more in detail. Here we will describe renderables in general, while in the coming section we'll talk about building layouts composed of multiple renderables.


This section focuses a bit on how renderables work under the hood. If you just want use Term and you don't care too much for how it works, skip ahead to the next section!

When you venture beyond styling simple strings, virtually every object you'll encounter will be a subtype of the AbstractRenderable type. We will call these objects renderables. Renderable types vary, but they all must have two fields: Segment and Measure:

A Segment is roughly equivalent to one line of text. Let's take something like this (printed out in your terminal):

│                    │

you can think of this as being made of three segments:

# 1

# 2
│                    │

# 3

When the renderable get's printed each of its segments is printed separately on a new line, giving the illusion of a single object (if we designed the segments correctly). In addition, the text stored by a Segment already has applied style information to it (i.e. markup tags are converted to ANSI codes), so it's ready to print!

In addition to a vector of segments, a renderable is defined by a Measure object. Roughly speaking, a Measure object stores information about the size of a renderable as it will appear in the terminal. Anything can have a measure: a string of text, a segment (the measure of its text) and a renderable (the combined measure of its segments). This information is crucial when we start putting multiple renderables together. For instance the renderable shown above is a Panel and a Panel can be created to fit a piece of text:

import Term: Panel

print(Panel("this is {red}RED{/red}"; fit=true))
│  this is RED  │

in order to do that Panel needs to know the size of the text it needs to fit, and that's done by taking its measure (note that the measure correctly ignores the style information to get the size of the text as it will be when printed out). Finally, we can think of the panel itself as having a Measure=(17, 3): 17 is the width of the panel and 3 its height (the number of segments). Again, this information is crucial when creating layouts of multiple renderables:

import Term: Panel

print(Panel(;height=3, width=6) * Panel(; height=5, width=12))
│    ││          │
╰────╯│          │
      │          │

but more on that in the next section.

Other renderables

Term comes with a few different types of renderables (we saw Panel already, but there's more), but the basic idea is that they are made of segments of text and have a measure. Each renderable has its own additional features on top of that, but those are described more in detail in dedicated pages (look left!). Briefly, we have Panel which creates stuff like what we've just seen, RenderableText which handles rendering text in the console (surprise!) and TextBox which is somewhat in between the two. Other renderables include things like Tree and, in the future, Table. Now lets look at how we can put multiple renderables together!