# Tikz: Node instead of newcommand: Making custom nodes

How do I make a special node out of the command \innblock?

\documentclass[tikz,border=5]{standalone}

\makeatletter
\newcommand\getwidthofnode[2]{%
\pgfextractx{#1}{\pgfpointanchor{#2}{east}}%
\pgfextractx{\pgf@xa}{\pgfpointanchor{#2}{west}}% \pgf@xa is a length defined by PGF for temporary storage. No need to create a new temporary length.
}
\makeatother

\newlength\IBwidth \setlength\IBwidth{8cm}
\newlength\IBheight \setlength\IBheight{6cm}

\newlength\blockroundedcorners\setlength\blockroundedcorners{0.5cm}
\newlength\blocklinewidth\setlength\blocklinewidth{3mm}

\colorlet{blocktitlebgcolor}{red}
\colorlet{blockbodybgcolor}{white}

\newcommand{\innblock}[4]{
\node[minimum size=\IBheight, minimum width=\IBwidth] (IBnode) at (#1,#2) {};
\begin{scope}[line width=\blocklinewidth, rounded corners=\blockroundedcorners, color=blocktitlebgcolor]
\draw[fill=blocktitlebgcolor] (IBnode.north west) rectangle (IBnode.south east);
\draw[fill=blockbodybgcolor] ([shift={(0,-\IBheaderheight)}]IBnode.north west) rectangle (IBnode.south east);
\newlength\mywidth\getwidthofnode{\mywidth}{IBnode}
\node[below right=\blocklinewidth, align=left, text=black, text width=\mywidth-3\blocklinewidth] at ([shift={(0,-\IBheaderheight)}]IBnode.north west) {#4};
\node[align=center, text=white, text width=\mywidth-3\blocklinewidth] at ([shift={(0,-0.5\IBheaderheight)}]IBnode.north) {#3};
\end{scope}
}

\begin{document}

\begin{tikzpicture}

% \node[innblock = Header ] at (2,1) {Body};

\end{tikzpicture}

\end{document}


I considered Use node text as argument for a macro but was not able to get it done.

• Wouldn't it be easier to do this with something like tcolorbox? – cfr Dec 8 '15 at 22:41
• @cfr: I'll have a look - however I nearly got it here... do you know how to get my second try right? – Make42 Dec 9 '15 at 10:48
• @cfr: tcolorbox only gets me so far: The issue remains how to get the text from the node into the tcolorbox. – Make42 Dec 9 '15 at 11:03
• Please ask your edit as a new question and delete it from this one. – Alenanno Dec 9 '15 at 13:21
• @Alenanno: Good point. I already asked the question, but you are right, that the edit does not belong here. – Make42 Dec 9 '15 at 13:23

You could use a multipart node. The white part inside is not rounded everywhere, but the rest works as your example in the question.

## Code

\documentclass[tikz,border=10pt]{standalone}

\usetikzlibrary{shapes.multipart}

\newlength\IBwidth \setlength\IBwidth{8cm}
\newlength\IBheight \setlength\IBheight{6cm}

\newlength\blockroundedcorners\setlength\blockroundedcorners{0.5cm}
\newlength\blocklinewidth\setlength\blocklinewidth{3mm}

\tikzset{
ib/.style={
rectangle split, rectangle split parts=2,
rectangle split draw splits=false,
rounded corners=\blockroundedcorners,
line width=\blocklinewidth,
minimum height=\IBheight,
minimum width=\IBwidth,
text width=\IBwidth-\blocklinewidth*2,
draw=#1,
rectangle split part fill={#1, none},
text=white, text centered
},
every two node part/.style={align=left, text=black},
}

\begin{document}
\begin{tikzpicture}

\node[ib={red}] {Header\nodepart{two}Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent gravida ligula non dolor sagittis, at sagittis felis gravida. Integer vitae lorem non sapien pellentesque euismod. Aliquam eleifend justo at eros vulputate pulvinar. Sed egestas felis massa, eu lacinia nisi vehicula vitae. Nunc molestie quis augue ut dictum. Mauris pellentesque purus id aliquet efficitur. Donec eget euismod quam. Fusce sed erat ligula. Quisque at dolor nisl. Ut eu gravida tellus, eget convallis mauris.};

\end{tikzpicture}
\end{document}

• Not quite what I wanted but still neat: +1. – Make42 Dec 9 '15 at 12:53

I understand that OP wants to define some titled and colorful boxes which can be places like nodes on some tikzpicture. tcolorbox produces title and colorful boxes, and these boxes can be used as text in TikZ nodes. Therefore, I propose something like:

\documentclass[tikz,border=2mm]{standalone}
\usepackage{lmodern}
\usepackage[most]{tcolorbox}

\tcbset{colframe=red!85!black, fonttitle=\bfseries}

\newtcolorbox{mybox}[2][]{
enhanced,
colframe=red,
fonttitle=\bfseries,
halign title=center,
boxrule=3mm,
height=5cm,
valign=top,
width=7cm,
interior titled code={
\fill[rounded corners=3mm, fill=white] (interior.north west) rectangle (interior.south east);
},
#1,
title=#2
}

\begin{document}

\begin{tikzpicture}

\draw (0,0) grid (8,12);

\node[above right, inner sep=0pt] at (0,0) {%
This is my new box made with \texttt{tcolorbox}.
\end{mybox}};

\node[above, inner sep=0pt] at (5,6) {%
This is my tcolorbox
\end{mybox}};
\end{tikzpicture}

\end{document}


which results

Not exactly what OP wants but it inblock style is now a .style with two parameters (header and node contents) and node coordinates can be used as at (x,y):

\node[inblock={Header}{Some longer text}] at (2,1) {};


In this case only one node is drawn with header and contents being two node label. For contents label a parbox with defined height and width has been used. Margins and parbox dimensions still need some work but the code is just a proposal.

\documentclass[tikz,border=5]{standalone}

\makeatletter
\newcommand\getwidthofnode[2]{%
\pgfextractx{#1}{\pgfpointanchor{#2}{east}}%
\pgfextractx{\pgf@xa}{\pgfpointanchor{#2}{west}}% \pgf@xa is a length defined by PGF for temporary storage. No need to create a new temporary length.
}
\makeatother

\newlength\IBwidth \setlength\IBwidth{8cm}
\newlength\IBheight \setlength\IBheight{6cm}

\newlength\blockroundedcorners\setlength\blockroundedcorners{0.5cm}
\newlength\blocklinewidth\setlength\blocklinewidth{3mm}

\newlength\innerblocktextwidth
\setlength\innerblocktextwidth{\IBwidth}

\newlength\innerblocktextheight
\setlength\innerblocktextheight{\IBheight}

\colorlet{blocktitlebgcolor}{red}
\colorlet{blockbodybgcolor}{white}

\begin{document}

\begin{tikzpicture}[
inblock/.style 2 args={
draw=blue,
minimum size=\IBheight,
minimum width=\IBwidth,
outstyle,
label={[outstyle,anchor=north,text=white]#1},
label={[instyle, anchor=south, yshift=.5\blocklinewidth]below:\parbox[t][\innerblocktextheight][t]{\innerblocktextwidth}{#2}},
},
outstyle/.style={
line width=\blocklinewidth,
color=blocktitlebgcolor,
rounded corners=\blockroundedcorners,
draw,
fill
},
instyle/.style={
rounded corners=\blockroundedcorners,
draw=blocktitlebgcolor,
fill=blockbodybgcolor
}]

\node[inblock={Header}{Some longer text}] at (2,1) {};
\end{tikzpicture}

\end{document}


• Interesting and valid alternative: +1. Long text (as my case has), is an issue though. But besides that I think it's pretty neat. – Make42 Dec 10 '15 at 9:09
• @Make42: I've updated my answer with what I think is a better solution. – Ignasi Dec 11 '15 at 11:12
• This is much improved. I think your code is more concise then mine. Both have different advantages. Yours wields the power of tcolorbox, mine the power of tikz (even more powerful, but more difficult). Yours has the advantage of being simpler, mine needs less code in the document... Hard to decide :-) (Don't think that I am biased.) – Make42 Dec 14 '15 at 18:31

I managed to do it. The opacity etc. is so you can see what is happening underneath easier:

\documentclass[tikz,border=5]{standalone}
\usepackage{blindtext}

\makeatletter
\newcommand\getwidthofnode[2]{%
\pgfextractx{#1}{\pgfpointanchor{#2}{east}}%
\pgfextractx{\pgf@xa}{\pgfpointanchor{#2}{west}}% \pgf@xa is a length defined by PGF for temporary storage. No need to create a new temporary length.
}
\makeatother

\newlength\IBwidth \setlength\IBwidth{8cm}
\newlength\IBheight \setlength\IBheight{1.7cm}

\newlength\blockroundedcorners\setlength\blockroundedcorners{0.5cm}
\newlength\blocklinewidth\setlength\blocklinewidth{3mm}

\colorlet{blocktitlebgcolor}{red}
\colorlet{blockbodybgcolor}{white}

\newlength\mywidth
\newcommand{\save}[1]{\gdef\lastsave{#1}#1}

\tikzset{
innblock/.style = {
alias=IBnode, minimum size=\IBheight, minimum width=\IBwidth, text width=\IBwidth-3\blocklinewidth,
append after command = {\pgfextra{
\begin{scope}[line width=\blocklinewidth, rounded corners=\blockroundedcorners, color=blocktitlebgcolor]
\getwidthofnode{\mywidth}{IBnode}
\draw[below right, fill=blocktitlebgcolor, opacity=0.5] ([shift={(0,\IBheaderheight+0.5\blocklinewidth)}]IBnode.north west) rectangle ([shift={(0,-0.5\blocklinewidth)}]IBnode.south east);
\draw[below right, fill=blockbodybgcolor, opacity=0.5] ([shift={(0,0.5\blocklinewidth)}]IBnode.north west) rectangle ([shift={(0,-0.5\blocklinewidth)}]IBnode.south east);
\node[below right=0mm, align=left, text=black, text width=\mywidth-2\blocklinewidth] at ([shift={(0,0.5\blocklinewidth)}]IBnode.north west) {\lastsave};
\node[align=center, text=white, text width=\mywidth-3\blocklinewidth] at ([shift={(0,0.5\IBheaderheight+0.5\blocklinewidth)}]IBnode.north) {#1};
\end{scope}
}}
}
}

\begin{document}

\begin{tikzpicture}

\node[innblock=Header2] at (0,-4.25) {\save{asadfasdfasdf \\ asadfasdfasdf asadfasdfasdf \\ a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i \\ asadfasdfasdf \\ asadfasdfasdf}};

\node[innblock=Header3] at (0,-10) {\save{bsbdfbsdfbsdf \\ bsbdfbsdfbsdf bsbdfbsdfbsdf \\ b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i \\ bsbdfbsdfbsdf \\ bsbdfbsdfbsdf \\ asdfasdf \\ asdfasdf asdfa \\ asdfasd \\asdfasd }};

\end{tikzpicture}

\end{document}


## Acknowledgements

Related questions that got the last roadblock out of the way for me (thanks to esdd):

Also essential:

Related questions (not contributing to this answer, but I am still thankful for the support, especially to percusse and Bordaigorl):

• This looks good. But you should attribute the code properly for the code you've borrowed from other authors. – cfr Dec 9 '15 at 22:19