vty-ui-1.0: doc/ch4/Table.tex
\section{Tables}
\label{sec:tables}
The \fw{Table} module provides a table layout widget which embeds
other widgets and provides full control over column and cell padding,
alignment, and cell borders.
The \fw{Table} creation function \fw{newTable} requires two parameters
which govern the overall table behavior:
\begin{itemize}
\item \textit{column specifications} -- a list of values specifying
how each column in the table is to behave, including its width
policy, alignment, and padding settings
\item \textit{border configuration} -- a value specifying how the
table's borders are to be drawn (if any)
\end{itemize}
Here is an example of a table with two columns and full borders:
\begin{haskellcode}
tbl <- newTable [column (ColFixed 10), column ColAuto] BorderFull
\end{haskellcode}
To add rows to the table, we use the \fw{addRow} function and the row
constructor \fw{.|.} to construct rows:
\begin{haskellcode}
n <- plainText "Name"
ph <- plainText "Phone Number"
addRow tbl $ n .|. ph
\end{haskellcode}
In the following sections we will go into more detail on the table
API.
\subsection{Column Specifications: the \fw{ColumnSpec} Type}
\label{sec:columnspecs}
\fw{newTable}'s column specification list dictates how many terminal
columns the \fw{Table} will have and how they will behave. The column
specification type, \fw{ColumnSpec}, specifies three properties of a
column:
\begin{itemize}
\item Width -- either a fixed number of columns, \fw{ColFixed}, or
automatically sized, \fw{Col\-Auto}.
\item Alignment -- left-aligned by default.
\item Padding -- no padding by default.
\end{itemize}
The width of a column dictates how many columns will be allocated to
it at rendering time. A \fw{ColFixed} column will be rendered in the
specified number of columns. A column with a \fw{ColAuto} width will
be allocated a flexible amount of width at rendering time.
For example, if a \fw{Table} with no borders is rendered in a region
with 80 columns and has two \fw{ColFixed} columns with 10 and 20
columns respectively and one \fw{ColAuto} column, the \fw{ColAuto}
column will be given $80 - (10 + 20) = 50$ columns of space in the
rendering process. A \fw{Table} may have any number of \fw{ColAuto}
columns; in general, the remaining space is divided evenly between
them.
The padding and alignment in the \fw{ColumnSpec} serve as the default
properties for each cell in the column unless a cell has overridden
either.
The \fw{ColumnSpec} type is an instance of the \fw{Paddable} type
class we saw in Section \ref{sec:padding}, so we can specify the
default \fw{Padding} for a column with the \fw{pad} function:
\begin{haskellcode}
newTable [column ColAuto `pad` (padAll 2)] BorderFull
\end{haskellcode}
The \fw{ColumnSpec} type is also an instance of the \fw{Alignable}
type class provided by the \fw{Table} module. This type class
provides an \fw{align} function which we can use to set the default
cell alignment for the column:
\begin{haskellcode}
newTable [column ColAuto `align` AlignRight] BorderFull
\end{haskellcode}
The \fw{align} function takes an \fw{Alignment} value. Valid values
are \fw{Align\-Left}, \fw{Align\-Center}, and \fw{Align\-Right}.
\subsection{Border Settings}
\fw{Table}s support three border configurations using the
\fw{BorderStyle} type. Valid values are as follows:
\begin{itemize}
\item \fw{BorderNone} -- no borders of any kind.
\item \fw{BorderFull} -- full borders on all sides of the table and in
between all rows and columns.
\item \fw{BorderPartial} -- borders around or in between some elements
of the table; this constructor takes a list of \fw{BorderFlag}s,
whose values are \fw{Rows}, \fw{Columns}, and \fw{Edges}.
\end{itemize}
A \fw{Table}'s border style cannot be changed once the \fw{Table} has
been created.
\subsection{Adding Rows}
The \fw{addRow} function provides a flexible API for adding various
types of values to table cells. The function expects an instance of
the \fw{RowLike} type class. This type class is intended to be
instanced by any type that contains a value that can be embedded in a
table cell. Any \fw{Widget a} is a \fw{RowLike}, so any widget can be
added to a table in a straightforward way:
\begin{haskellcode}
t <- plainText "foobar"
addRow tbl t
\end{haskellcode}
In addition, empty cells can be created with the \fw{emptyCell}
function:
\begin{haskellcode}
addRow tbl emptyCell
\end{haskellcode}
The above examples work in the case where the \fw{Table} has only one
column; to construct rows for \fw{Table}s with multiple columns, we
use the row constructor, \fw{.|.}, which takes any two \fw{RowLike}
values and constructs a row from them:
\begin{haskellcode}
t1 <- plainText "foo"
t2 <- plainText "bar"
addRow tbl1 $ t1 .|. t2 -- tbl1 has two columns
t3 <- plainText "baz"
addRow tbl2 $ t1 .|. t2 .|. t3 -- tbl2 has three columns
\end{haskellcode}
The only restriction on table cell content is that any widget added to
a table cell \textit{must not grow vertically}. If it does,
\fw{addRow} will throw a \fw{TableError} exception.
\subsection{Default Cell Alignment and Padding}
The \fw{Table} stores default cell alignment and padding settings
which apply to all cells in the table. These settings are set with
the following functions:
\begin{itemize}
\item \fw{setDefaultCellAlignment} -- sets the default \fw{Alignment}
used for all cells in the table.
\item \fw{setDefaultCellPadding} -- sets the default \fw{Padding}
value used for all cells in the table.
\end{itemize}
We can override these settings on a per-column basis by setting
\fw{Alignment} and \fw{Padding} on the \fw{ColumnSpec} values as we
saw in Section \ref{sec:columnspecs}.
\begin{haskellcode}
setDefaultCellPadding tbl (padLeft 1)
setDefaultCellAlignment tbl AlignCenter
\end{haskellcode}
As we will see in the following section, we can even override these
settings on a per-cell basis.
\subsection{Customizing Cell Alignment and Padding}
By default, each table cell uses its column's alignment and padding
settings. If the column's \fw{ColumnSpec} has no alignment or padding
settings, the table-wide defaults will be used instead. However, it
is possible to customize these settings on a per-cell basis.
Every widget in a \fw{Table} is ultimately embedded in the
\fw{TableCell} type. This type holds the widget itself and any
customized alignment and padding settings. The \fw{TableCell} type is
an instance of the \fw{Paddable} and \fw{Alignable} type classes so we
can use the familiar \fw{pad} and \fw{align} functions to pad and
align the \fw{TableCell}.
To customize a cell's properties, we must first wrap the cell widget
in a \fw{TableCell} with the \fw{customCell} function:
\begin{haskellcode}
t <- plainText "foobar"
addRow tbl $ customCell t
\end{haskellcode}
Then we can use \fw{pad} and \fw{align} on the \fw{TableCell}:
\begin{haskellcode}
t <- plainText "foobar"
addRow tbl $ customCell t `pad` (padAll 1) `align` AlignRight
\end{haskellcode}
\subsubsection{How Cell Alignment Works}
Cell alignment determines how remaining space will be used when a
cell's widget is rendered. The default poilcy, \fw{AlignLeft},
indicates that when a cell's widget is rendered, it will be
right-padded with a space-filling widget so that it takes up enough
on-screen columns to fill the width specified by the \fw{Table}'s
\fw{ColumnSpec}. The \fw{AlignRight} and \fw{AlignCenter} settings
behave similarly.
What this means is that the alignment settings do not dictate
\textit{how} the contents of each cell are laid out; they only dictate
how the left-over space is used when a cell widget does not fill the
table's column. In most cases this distinction is effectively
unimportant, but in some cases it may be helpful to understand.
Consider a table cell which contains an \fw{Edit} widget. \fw{Edit}
widgets grow horizontall. Any \fw{Edit} widget placed in a table cell
will always fill it, so alignment settings will not affect the result.
However, if the \fw{Edit} widget is constrained with a ``fixed''
widget as described in Section \ref{sec:fixed}, if any space is left
over, the widget will be padded according to the alignment setting.
\subsubsection{Growth Policy}
\fw{Table}s do not grow vertically but will grow horizontally if they
contain any \fw{ColAuto} columns.