TikZ rectangle split parts with fixed height

The following solution is a compromise between \nodepart{second} \hphantom{null} (or an extra macro) and the condition that even not-empty parts have an specified width.

The style data+ takes one mandatory argument and sets the dimension of empty parts to the dimension of its argument.

In your example you would need data+=null. Of course, if the actual content of the second part of the last node changes, you would have to change the argument given to data+ (though this could be a macro too).

Code

\documentclass{article}
\usepackage{tikz,calc}
\usetikzlibrary{shapes}
\tikzset{
    data/.style={
        draw,
        rectangle split,
        rectangle split parts=2,
        text centered,
    },
    data+/.style={
        data,
        rectangle split every empty part={},% resets empty-part macro (explanation below)
        rectangle split empty part width=\widthof{#1},
        rectangle split empty part height=\heightof{#1},
        rectangle split empty part depth=\depthof{#1},
    },
}
\newcommand{\data}{data \nodepart{second} \phantom{null}}
\begin{document}
\begin{tikzpicture}[node distance=2cm]
    \node [data+={null}]        (A)    {data};
    \node [data, right of=A]    (B)    {\data};
    \node [data, right of=B]    (C)    {data \nodepart{second} null};
\end{tikzpicture}
\end{document}

Output

enter image description here

Excursus: -1ex

The definition of the data+ style was previously

data+/.style={
    data,
    rectangle split empty part width=\widthof{#1}-1ex,
    rectangle split empty part height=\heightof{#1},
    rectangle split empty part depth=\depthof{#1},
},

The OP rightly asks

[W]hy do you have to subtract 1ex from the width?

PGF/TikZ has one special macro that is inserted for an empty node-part if /pgf/rectangle split ignore empty parts is false(which seems to be the default contrary to the PGF manual), otherwise we wouldn’t even get a box.
The name of this macro is \pgf@lib@sh@rs@every@emptypart (for short: empty-part macro).

One would expect that PGF stores away the lengths given to rectangle split empty part <width|height|depth> keys and does not build this macro with the latest dimensions until it is needed. But this is not the case.
Every time rectangle split empty part <width|height|depth> is used a rule of the given width, height or depth is created and added  to the empty-part macro (all other length parameters of that rule are 0pt, meaning they are invisible (e.g. \strut)).

The problem arises that rules (even invisible ones) are getting stacked horizontally. Rules with a height or depth do not get stacked vertically so they don’t accumulate extra height/depth, but rules with a width (horizontal rules) accumulate.

But wait! We only used those keys once! Yes, but no.
As a matter of fact they are used once before, namely during the initialization.
The file pgflibraryshapes.multipart.code.tex reads on lines 370, 378 and 386:

rectangle split empty part width=1ex,%  line 370
rectangle split empty part height=1ex,% line 378
rectangle split empty part depth=0ex,%  line 386

When we start we have a square box of 1ex × 1ex. If we add a vertical rule of 2ex we get a box of 1ex × 2ex, but if we add a horizontal rule of 2ex we get a box of 3ex × 1ex.

A picture worth than thousand words?

I constructed an example, where

  • the initial box is empty:

    \def\pgf@lib@sh@rs@every@emptypart{}% clean slate
    
  • and the rules have actually a “width” (orthogonal to its “length”), i.e. a horizontal rule has a height (actually a height and a depth so that the middle of the line lies on the baseline), and a vertical rule has a width.

Not-MWE

rs width is a short-cut for rectangular split empty parts width, the same holds for rs height and rs depth.

\node[rectangle split ignore empty parts=false]                                          (a) {a};
\node[right of=a,  rs width =  1ex]                                                      (b) {b};
\node[right of=b, rs height =  3ex]                                                      (c) {c};
\node[right of=c,  rs depth = .5ex]                                                      (d) {d};
\node[below of=a,   rs width = 1ex,  rs width = \widthof{null}]           [yshift=-.5cm] (e) {e};
\node[right of=e,  rs height = 1ex, rs height = 2ex, rs height = .5ex]                   (f) {f};
\node[right of=f,   rs depth = 1ex,  rs depth = 2ex,   rs depth = 3ex]                   (g) {g};
\node[right of=g,   rs width = 1ex,  rs depth = 2ex,  rs height = 1ex, rs width = 2.5ex] (h) {h};

Output

The rules are:

  • baseline: black
  • horizontal (width): red
  • vertical (height): green
  • vertical (depth): blue

enter image description here

Is there a better solution?

Two come to mind:

  1. instead of subtracting hard-coded 1ex we could subtract the current actual width

    rectangle split empty part width=\widthof{#1}-\widthof{\pgf@lib@sh@rs@every@emptypart}
    

    which would work quite okay as long as its content is not wider than #1.

  2. Overwriting the empty-part macro.

There is a undocumented key rectangle split every empty part that is not used once in PGF and TikZ. Its definition is

rectangle split every empty part/.store in=\pgf@lib@sh@rs@every@emptypart

We cannot do rectangle split every empty part={\phantom{null}} because TikZ switched to a \nullfont that swallows everything. (Have you ever tried to just write something in a tikzpicture environment?). This is the same reason, I used calc’s \<dimen>of macros, they escape this cleverly.

We could fill this empty part macro with rules (not again!) overwriting any existing content, but then we could just use our old solution without -1ex by just removing any content.

Code

\tikzset{
    data+/.style={
        data,
        rectangle split every empty part={},% resets empty-part macro
        rectangle split empty part width=\widthof{#1},
        rectangle split empty part height=\heightof{#1},
        rectangle split empty part depth=\depthof{#1},
    },
}

After trying out the suggestions in the comments to my question, I found it the easiest to stick with my first attempt using \phantom, so here how it lookes in code.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\tikzstyle{data}=[rectangle split,rectangle split parts=2,draw,text centered]
\newcommand{\data}{data \nodepart{second} \phantom{null}}

\begin{document}
\begin{tikzpicture}[node distance=2cm]
    \node [data]                (A)    {\data};
    \node [data, right of=A]    (B)    {\data};
    \node [data, right of=B]    (C)    {data \nodepart{second} null};
\end{tikzpicture}
\end{document}

Which results in:

Output

I needed data \nodepart{second} \phantom{null} a couple of times, therefore I thought it would be nice to define an own command for this, which might seem like a bit much in the example, but for the real application it was very useful. Have a look:

List

I post this for it may help someone with a similar problem.

Tags:

Nodes

Tikz Pgf