# tikz: a big box with fixed width containing smaller boxes

I want to render with tikz something like :

The surrounding big box should behave somehow like a minipage : the width is fixed, and the height should grow if we add sufficiently small boxes inside. For the moment, i use the following (simplified) MWE to produces that :

\begin{tikzpicture}
\tikzstyle{bigbox} = [minimum height=2cm,draw,rounded corners, rectangle]
\tikzstyle{box} = [minimum height=0.8cm,draw,rounded corners, draw,rectangle]
\node[bigbox](outbox)%
{
\begin{minipage}{3cm}
\tikz{\node[box]{12};}
\tikz{\node[box]{12};}
\tikz{\node[box]{12};}
\tikz{\node[box]{12};}
\end{minipage}
};
\end{tikzpicture}


As you can notice, i nest \tikz's inside the tikzpicture, which is bad, as i just learnt in this post from Andrew Stacey. He advises to use the "fit" option from \usetikzlibrary{fit}, but i don't see how to do it, in this case.

-
The fit library would require that you position your boxes manually first. However, it would be possible to code a macro or environment which takes the nodes and position them automatically. –  Martin Scharrer Jan 6 '12 at 11:05
Also note that \tikzstyle is deprecated. The official way is \tikzset{bigbox/.style={<style>},..}. –  Martin Scharrer Jan 6 '12 at 11:06
I don't see a problem with nesting here. Is there something that you don't like with your current solution? Or do you just want to change it based on Andrew saying it's bad. Unless you run into problems with inheritance of options, I don't see a reason not to nest them in this case. –  Roelof Spijker Jan 6 '12 at 11:09
This is pretty much what the matrix node style is for. The inner nodes are laid out in a grid (which can be customised) and then you have a "big" node outside (the matrix node) which you can also style. –  Loop Space Jan 6 '12 at 11:15
Ouch. That's going to be hard. I can see why you tried nesting stuff. Can the inner boxes vary in width (in the examples, they are all the same size)? What about alignment? Is everything to be left aligned? –  Loop Space Jan 6 '12 at 11:49

Mark has answered the bit about getting the outer rectangle sorted once the inner stuff is in place (given the constraints, the fit and layer method seems the better fit). What I'm going to tackle is getting the inner boxes to position themselves with "line wrapping". This is not a complete solution, but if it seems along the right lines then I think that the rest would not be hard to do (but I want to check before doing that).

My comment to the question (with the quote from the real Italian Job) was along the right lines but just not quite far enough along. The positioning library can take quite complicated expressions - it is possible to do things like above={(\x > \y ? 3cm : -2cm)} of a.south - but it turns out that these are computed before the node is laid out. All the complicated positioning is done by the node code, not by the positioning code.

So we have to co-opt the node code to our nefarious purposes. Fortunately, this is relatively easy (if a little tedious to do in full). Nodes are positioned by specifying an anchor. That anchor is placed at (0,0) (the positioning library, or just TikZ, sets things up so that as far as the node is concerned, (0,0) is where the node is "at"). So if we want to place the node somewhere a little different, we simply define a rather complicated anchor. This entails either defining a new shape or using Martin Scharrer's code for adding new anchors to an existing shape. I've gone for the former and defined a "moveable rectangle". This rectangle has the property that its north west anchor can depend on a parameter. Well, actually a little computation. This computation can use the information about the node size, since it is computed while the node is being laid out. My computation is:

\pgfmathparse{(\mrwidth > 1cm ? "(a.north east)" : "(a.south west)")}
\let\mrvector=\pgfmathresult


This tests a macro called \mrwidth which happens to be set to the width of the node (mr = "moveable rectangle") (obviously, one could adapt it to make the height available as well). If the width is big, it returns (a.north east) and if small, (a.south west). The result is saved as \mrvector. This code is executed while the node is working out the north west anchor of the rectangle and the anchor is shifted according to the result. As node coordinates are "absolute", the result is that the north west anchor of the current node is placed at either the north east or the south west of the a node depending on its width. If the coordinates had been things like (0,2cm), this would have indicated a relative adjustment.

So the idea is to make a slightly more complicated test which looks to see if the width of the node is going to take it outside the given width and if so, anchor the node on the next line. This would need a little more work, but not overmuch, as there will typically be several nodes involved in the computation. But that's just bookkeeping.

I'll put the full code at the end of this answer. If it were all packaged up in a nice package, the syntax would be something like:

\begin{document}
\begin{tikzpicture}
\node[box] (a) {A};
\node[box,moveable rectangle,anchor=north west,moveable] at (a.south east) (b) {B};
\node[box,moveable rectangle,anchor=north west,moveable] at (a.south east) (c) {ABCDEFG};
\end{tikzpicture}
\end{document}


with result:

As you can see, the specifications to the two nodes are exactly the same, but one ends up alongside the a node and one just below it.

Here's the code so far. I've currently only modified the north west anchor. Obviously, the others will need doing as well. And stress testing and probably some assumptions I've made will be shown to be completely ludicrous ...

\documentclass{article}
%\url{http://tex.stackexchange.com/q/40234/86}
\usepackage{tikz}
\makeatletter
\tikzset{
box/.style={
minimum height=0.8cm,
draw,
rounded corners,
rectangle
},
moveable rectangle/condition/.code={%
\pgfmathparse{(\mrwidth > 1cm ? "(a.north east)" : "(a.south west)")}
\let\mrvector=\pgfmathresult
},
moveable/.is if=mr@moveable,
}
\def\mrvector{(0,0)}
\newif\ifmr@moveable
\pgfdeclareshape{moveable rectangle}{%
\inheritsavedanchors[from=rectangle]%
\anchor{north west}{%
\ifmr@moveable
\northeast
\pgf@xa=\pgf@x
\southwest
\edef\mrwidth{\the\pgf@xa}%
\pgfkeys{/tikz/moveable rectangle/condition}%
\tikz@scan@one@point\pgfutil@firstofone\mrvector
\pgf@xa=-\pgf@x
\pgf@ya=-\pgf@y
\northeast
\southwest
\pgf@x=\pgf@xa
\pgf@y=\pgf@ya
\else
\northeast
\pgf@ya=\pgf@y
\southwest
\pgf@y=\pgf@ya
\fi
}
\inheritbackgroundpath[from=rectangle]
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[box] (a) {A};
\node[box,moveable rectangle,anchor=north west,moveable] at (a.south east) (b) {B};
\node[box,moveable rectangle,anchor=north west,moveable] at (a.south east) (c) {ABCDEFG};
\end{tikzpicture}
\end{document}

-
Smells like a TeX.SX package... –  percusse Jan 6 '12 at 22:54
Brilliant! There needs to be a +5 button for stuff like this. –  qubyte Jan 7 '12 at 10:11

The following is crude, and perhaps better done using a matrix, but it's here to demonstrate how to use a fitted box in the background.

\documentclass[10pt]{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds,fit}

\begin{document}

\begin{tikzpicture}[outer sep=0.05cm,node distance=0.8cm,]
\tikzstyle{bigbox} = [draw=blue!50, thick, fill=blue!10, rounded corners, rectangle]
\tikzstyle{box} = [minimum size=0.6cm, rounded corners,rectangle, fill=blue!50]
%
\node[box] (11) {1};
\node[box,right of=11] (12) {2};
\node[box,right of=12] (13) {3};
\node[box,below of=11] (21) {4};
\node[box,right of=21] (22) {5};
\node[box,right of=22] (23) {6};
%
\begin{pgfonlayer}{background}
\node[bigbox] [fit = (11) (23)] {};
\end{pgfonlayer}
%
\end{tikzpicture}

\end{document}


Note that the fit = (11) (22) makes sure the box contains the top left and bottom right boxes. For more elaborate diagrams you can more node names to this to fit.

A similar result can be obtained more cleanly using a TikZ matrix. Another benefit is that there is no need for fitting, since the matrix itself takes the bigbox style.

\begin{tikzpicture}
\tikzstyle{bigbox} = [draw=blue!50, thick, fill=blue!10, rounded corners, rectangle]
\tikzstyle{box} = [minimum size=0.6cm, rounded corners,rectangle, fill=blue!50]
%
\matrix[row sep=2mm, column sep=2mm, inner sep=2mm, bigbox, every node/.style=box] {
\node {1}; & \node {2}; & \node {3};\\
\node {4}; & \node {5}; & \node {6};\\
};
%
\end{tikzpicture}


As noted by percusse, this may be further simplifed by adding the matrix library and using the matrix of nodes option of the matrix.

\begin{tikzpicture}
\tikzstyle{bigbox} = [draw=blue!50, thick, fill=blue!10, rounded corners, rectangle]
\tikzstyle{box} = [minimum size=0.6cm, rounded corners,rectangle, fill=blue!50]
%
\matrix[row sep=2mm, column sep=2mm, inner sep=2mm, bigbox, matrix of nodes, every node/.style=box] {
1 & 2 & 3\\
4 & 5 & 6\\
};
%
\end{tikzpicture}

-
In this particular case, I would go for a matrix as the layout is grid-like and the matrix node gets the foreground/background right without layers. However, this is a good demonstration of how to do this with fit and layers for when the matrix node type is too restrictive. –  Loop Space Jan 6 '12 at 11:17
@AndrewStacey: I'm probably going to add a matrix version below the basic one. What do you reckon? I don't want to muddy the waters... –  qubyte Jan 6 '12 at 11:23
You should definitely add a matrix version. If you don't, someone else ought to (I won't, as I'll be busy duelling wh1t3). If you have time to do it, then there's no issue having them in the same answer as far as I can see. One is not "better" than the other, just suited to different circumstances. –  Loop Space Jan 6 '12 at 11:26
@AndrewStacey: Cool, I added a matrix example too. –  qubyte Jan 6 '12 at 11:39
If you use the matrix library and include the matrix of nodes option to the matrix, you don't need to type \node for each entry. –  percusse Jan 6 '12 at 12:17