2

I'd like to define a TikZ node style that sets the node shape to rectangle split and automatically adds a first part with content defined by the style. The usual node text appears in the second part. Is there a way to do this, or something similar to this?

Desired behavior

Code:

\node[achtung] at (1, 2) {Falling rocks};

Output:

( Achtung | Falling rocks )

Attempts

I've tried

achtung/.append style={
  execute at begin node={Achtung\nodepart{two}},
  rectangle split,
  rectangle split horizontal,
  rectangle split parts=2
}

and more sophisticated versions, like

achtung/.append style={
  execute at begin node={Achtung\protect\nodepart{two}\protect\begingroup},
  execute at end node={\protect\endgroup},
  rectangle split,
  rectangle split horizontal,
  rectangle split parts=2,
  every one node part/.style={
    execute at begin node={},
    execute at end node={}
  },
  every two node part/.style={
    execute at begin node={},
    execute at end node={}
  }
}

They all lead to an "Extra }, or forgotten \\endgroup.\n\\pgfutil@reserved@c ->\\egroup \n" error.

Versions using \pgfnodeparttwobox compile, but don't give the desired behavior.

Minimal not-working example

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{shapes.multipart}

\tikzset{
  achtung/.append style={
    execute at begin node={Achtung\nodepart{two}}, % remove this line to compile successfully
    rectangle split,
    rectangle split horizontal,
    rectangle split parts=2,
    draw
  }
}

\begin{document}
\begin{tikzpicture}
\node[achtung] {Falling rocks};
\end{tikzpicture}
\end{document}
7
  • 1
    Interesting. Can you please embed one of your styles in a short MWE, which compiles? Thank you
    – MS-SPO
    Dec 16, 2023 at 20:33
  • 1
    @MS-SPO: Sure. Done.
    – Vectornaut
    Dec 16, 2023 at 20:46
  • Would a syntax like \node[achtung={Falling rocks}]; be acceptable? Dec 16, 2023 at 20:48
  • @TorbjørnT.—Yes, but I'd like to allow \node[achtung=...] at (1, 2) ...;, which I don't think is possible if you use node contents to insert the desired text.
    – Vectornaut
    Dec 16, 2023 at 20:58
  • 1
    Yes, that might be a drawback .. Dec 16, 2023 at 21:48

3 Answers 3

2

The hook execute at begin node is simply too early to play around with nodeparts. There really is no appropriate hook to use here. We could try to misuse one of the other macros that will be executed right at the start – they deal with horizontal alignments and nested TikZ pictures (?) – …

But instead of messing around with them and creating incompatibilities I'll just add our own hook print at begin node which acts similar to node contents but instead of replaces it simply prefixes what comes between { and }.

(This hook will not be reset for when a TikZ picture is part of the node content but it's rarely a good idea to do that in the first place.)

Code

\documentclass[tikz]{standalone}
%\documentclass{article}
%\usepackage{tikz}

\ExplSyntaxOn \makeatletter
\tl_replace_once:Nnn \tikz@do@fig
  { \ignorespaces }
  { \pgfkeysvalueof{/tikz/print~at~begin~node} \ignorespaces }
\makeatother \ExplSyntaxOff
\pgfkeyssetvalue{/tikz/print at begin node}{}

\usetikzlibrary{shapes.multipart}
\tikzset{
  achtung/.append style={
    print at begin node = Achtung \nodepart{two},
    rectangle split, rectangle split horizontal, rectangle split parts=2, draw}}
\tikzset{
  gnutcha/.append style={
    print at begin node = \nodepart{two} Achtung \nodepart{one},
    rectangle split, rectangle split horizontal, rectangle split parts=2, draw}}
\begin{document}
\tikz \node[achtung]            {Falling rocks}
       node[gnutcha] at (0, -1) {Falling rocks};

\tikz \node[achtung]            {Falling rocks \nodepart{text} You're not the }
       node[achtung] at (0, -1) {Falling rocks \nodepart{one}    boss of me.  };
\end{document}

Output

enter image description here

enter image description here

3
  • 1
    The \ignorespaces is irrelevant in this use case because we always end the new hook with \nodepart{…} which has its own \ignorespaces (and is basically \tikz@do@fig for nodeparts). Since PGFKeys also strips its values of spaces, the explicit spaces\space or \ might be helpful. Since TikZ is bugged when it comes to nodeparts and ends I'm not even attempting a print at end node hook. Dec 20, 2023 at 23:22
  • Whoa, I never knew you could reach into existing commands like this! I've seen so many suggestions to adjust a command by copying its source code with some small change, and I've always wondered if there was a better way. Here, are you taking advantage of the coincidence that the first \ignorespaces in \tikz@do@fig happens to come right before the node content?
    – Vectornaut
    Dec 21, 2023 at 2:28
  • 1
    @Vectornaut Yes, I've also taken advantage that \tikz@do@fig doesn't take an argument, otherwise etoolbox, other patching package or other tricks would be necessary. We could've also used \tl_put_right:Nn leaving an extra \ignorespaces in it because it is also at the end of \tikz@do@fig's definition. Dec 22, 2023 at 14:40
2

Using node contents you could have node contents={Achtung\nodepart{two}#1} in the style, and use it as achtung={Falling rocks}. As noted in comments using node contents means you can't do\node [..] at (x,y)..., though \node at (x,y) [...] does work.

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{shapes.multipart}

\tikzset{
  achtung/.append style={
    rectangle split,
    rectangle split horizontal,
    rectangle split parts=2,
    draw,
    node contents={Achtung\nodepart{two}#1}
  },
  achtung/.default=
}

\begin{document}
\begin{tikzpicture}
\node[achtung={Falling rocks}];
\end{tikzpicture}
\end{document}

Output image: [Achtung | Falling rocks]

1
  • 1
    This is the method I've gone with for now, but I'm keeping the question open in case someone finds a way to allow more typical-looking node syntax.
    – Vectornaut
    Dec 16, 2023 at 23:03
1

With a command ?

EDIT: with the Alan Munn' comment By adding the position of the node as a parameter and the D(){} specification for the #2 parameter

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{shapes.multipart}
\NewDocumentCommand{\mynode}{ O{achtung} d() m }{
\IfNoValueTF{#2}
    {\path node[#1,right]{#1\nodepart{two}#3};}
    {\path (#2) node[#1,right]{#1\nodepart{two}#3};}
}
\tikzset{
    achtung/.style={
        rectangle split,
        rectangle split horizontal,
        rectangle split parts=2,
        draw,
    }
}
\begin{document}
\begin{tikzpicture}
\draw[help lines](0,0)grid(3,2);
\mynode[achtung] (2,1) {Falling rocks}
\mynode (0,0) {achtung is the default style}
\end{tikzpicture}
\end{document}

enter image description here

9
  • I agree that this is likely the most robust solution, but the OP wants to allow the \node[] at (x,y) {} syntax, which your command doesn't support.
    – Alan Munn
    Dec 17, 2023 at 16:21
  • 1
    You're reading this wrong. The D described in interface3 is the argument specifier in an Expl3 function, e.g. \foo:D. It is not the delimited argument specifier of the xparse \NewDocumentCommand specification. That D (the one I'm using) allows you to specify a delimited optional argument. So using D(){} is not using a deprecated functionality. This is an unfortunate overlapping of distinct uses.
    – Alan Munn
    Dec 20, 2023 at 14:30
  • 1
    The D argument I'm using is described in usrguide p.3.
    – Alan Munn
    Dec 20, 2023 at 14:40
  • 1
    I'd rather use the d() argument specifier, and then use \IfNoValueF{#1}{(#1)} (or use a required argument in parentheses) instead of D(){}, because if you omit the argument you'd get an error.
    – Skillmon
    Dec 20, 2023 at 14:53
  • 1
    I've taken the liberty to fix my bad suggestion with @Skillmon's superior one.
    – Alan Munn
    Dec 20, 2023 at 15:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .