1

I am working at a new beamer theme and I am wondering if it is possible to retrieve the height of the last shipped out beamercolorbox. To set up a reasonable minimal (although horribly looking) working example, let us focus on the frametitle template and let us assume (like it is in many themes) that the frame title is basically put into a beamercolorbox.

\documentclass{beamer}
\usetheme{Madrid}

\makeatletter
\defbeamertemplate*{frametitle}{myFrametitle}[1][left]
{
  \begin{beamercolorbox}[sep=0.3cm,wd=\textwidth]{frametitle}
    \usebeamerfont{frametitle}%
    \strut\insertframetitle\strut\par%
    {%
      \ifx\insertframesubtitle\@empty%
      \else%
      {\usebeamerfont{framesubtitle}\usebeamercolor[fg]{framesubtitle}\insertframesubtitle\strut\par}%
      \fi
    }%
  \end{beamercolorbox}%
  %-----
  % Here I would like to have a length with the height of the
  % above beamercolorbox, to be used to adapt dynamically stuff
  %-----
}
\makeatother

\setbeamertemplate{frametitle}[myFrametitle]

\begin{document}

    \begin{frame}{Frame title}
        A frame
    \end{frame}

    \begin{frame}{Frame title}{with subtitle}
        Another frame
    \end{frame}

 \end{document}

Is there an elegant way to retrieve the height of the box in the last beamercolorbox environment?

More information

  • Reading the beamerbasecolor.sty file, I thought that I might redefine the beamercolorbox environment adding a line like

    \global\setlength{\@lastbeamercolorboxheight}{\ht\beamer@tempbox}%
    

    before each \box\beamer@tempbox%, where \@lastbeamercolorboxheight would be an internal length of my theme. However, I do not like the idea of copying tons of beamer code just for a minimal add-on and, moreover, it does not look neither so elegant nor the way to go (see below to have a concrete example of this approach).

  • I am aware that I might use the ht and dp keys of the beamercolorbox to fix the height of the box, but this is not a valid approach in my case. It is more like, the user puts whatever she/he want in the frame title/subtitle and I act dynamically accordingly.

  • I am glad to receive any general hint and possibly slightly different approaches are welcome.

  • If the elegant solution is not to use a beamercolorbox at all, well, I will then think to an alternative from a more general perspective.

Redefining the beamercolorbox environment

Although this is not an elegant way to retrieve the height of the box, it gives access to the desired length. However, there are probably many drawbacks, but the code here might help readers suggesting better approaches.

\documentclass{beamer}
\usetheme{Madrid}

\makeatletter

\newlength{\@lastbeamercolorboxheight}

\renewenvironment{beamercolorbox}[2][]{% taken from beamerbasecolor.sty
  \begingroup%
    \def\beamer@colbox@coladd{0pt}%
    \def\beamer@vmode{\leavevmode}%
    \setkeys{beamercolbox}{%
      wd=\textwidth,ht={},dp={},%
      leftskip=0pt,rightskip=0pt plus1fil,%
      sep=0pt,colsep=0pt,colsep*=0pt,%
      shadow=false,rounded=false,ignorebg=false}%
    \setkeys{beamercolbox}{#1}%
    \ifbeamercolorempty[bg]{#2}{\@tempswafalse}{\@tempswatrue}%
    \ifbeamer@colbox@ignorebg\@tempswafalse\fi%
    \def\beamer@colbox@color{#2}%
    \hsize=\beamer@colbox@wd%
    \setbox\beamer@tempbox=\hbox\bgroup\vbox\bgroup%
      \leftskip=\beamer@colbox@ls%
      \advance\leftskip by\beamer@colbox@sep%
      \rightskip=\beamer@colbox@rs%
      \advance\rightskip by\beamer@colbox@sep%
      \ifbeamer@colbox@ignorebg%
        \colorlet{beamer@temp@color}{bg}%
        \usebeamercolor[fg]{#2}%
        \colorlet{bg}{beamer@temp@color}%
      \else%
        \usebeamercolor[fg]{#2}%
      \fi%
      \if@tempswa%
        \advance\leftskip by\beamer@colbox@colsep%
        \advance\rightskip by\beamer@colbox@colsep%
        \ifdim\beamer@colbox@colsep=0pt\else\vskip\beamer@colbox@colsep\fi%
        \ifdim\beamer@colbox@colseps=0pt\else\vskip\beamer@colbox@colseps\fi%
      \fi%
      \ifdim\beamer@colbox@sep=0pt\else\vskip\beamer@colbox@sep\fi%
      \beamer@vmode\ignorespaces}{%
      \ifdim\beamer@colbox@sep=0pt\else\vskip\beamer@colbox@sep\fi%
      \if@tempswa\ifdim\beamer@colbox@colsep=0pt\else\vskip\beamer@colbox@colsep\fi\fi%
      \if@tempswa\ifdim\beamer@colbox@colseps=0pt\else\vskip\beamer@colbox@colseps\fi\fi%
    \egroup\egroup%
    \wd\beamer@tempbox=\hsize%
    \@tempdima=\wd\beamer@tempbox%
    \ifx\beamer@colbox@ht\@empty%
    \else%
      \ht\beamer@tempbox=\beamer@colbox@ht%
    \fi%
    \ifx\beamer@colbox@dp\@empty%
    \else%
      \dp\beamer@tempbox=\beamer@colbox@dp%
    \fi%
    \ifbeamer@colbox@rounded%
      \if@tempswa%
        \begin{beamerboxesrounded}[%
          shadow=\beamer@colbox@shadow,%
          lower=\beamer@colbox@color,%
          upper=normal text,%
          width=\beamer@colbox@wd]{}%
          \global\setlength{\@lastbeamercolorboxheight}{\ht\beamer@tempbox}% <--- ADDED
          \box\beamer@tempbox%
        \end{beamerboxesrounded}%
      \else%
        \ifdim\@tempdima>\textwidth%
          \setbox\beamer@tempbox=\hbox to\textwidth{\hss\box\beamer@tempbox\hss}%
        \fi%
        \global\setlength{\@lastbeamercolorboxheight}{\ht\beamer@tempbox}%  <--- ADDED
        \box\beamer@tempbox%
      \fi%
    \else%
      \if@tempswa\setbox\beamer@tempbox=\hbox{\vbox{%
        \usebeamercolor{\beamer@colbox@color}%
        \advance\hsize by \beamer@colbox@colseps\relax%
        \advance\hsize by \beamer@colbox@colseps\relax%
        \hskip-\beamer@colbox@colseps%
        \fboxsep=0pt\colorbox{bg}{%
          \hskip\beamer@colbox@colseps%
          \hbox{\box\beamer@tempbox}%
          \hskip\beamer@colbox@colseps%
        }%
        \hskip-\beamer@colbox@colseps%
      }}\fi%
      \ifdim\@tempdima>\textwidth%
        \setbox\beamer@tempbox=\hbox to\textwidth{\hskip0pt minus\beamer@leftmargin\relax\box\beamer@tempbox\hskip0pt minus\beamer@rightmargin\relax}%
      \fi%
      \global\setlength{\@lastbeamercolorboxheight}{\ht\beamer@tempbox}%  <--- ADDED
      \box\beamer@tempbox%
    \fi%
  \endgroup%
}

\defbeamertemplate*{frametitle}{myFrametitle}
{
  \begin{beamercolorbox}[sep=0.3cm,wd=\textwidth]{frametitle}
    \usebeamerfont{frametitle}%
    \strut\insertframetitle\strut\par%
    {%
      \ifx\insertframesubtitle\@empty%
      \else%
      {\usebeamerfont{framesubtitle}\usebeamercolor[fg]{framesubtitle}\insertframesubtitle\strut\par}%
      \fi
    }%
  \end{beamercolorbox}%
  %-----
  % Here the length \@lastbeamercolorboxheight is set to the height of the box above
  \textcolor{black}{\the\@lastbeamercolorboxheight}
}
\makeatother

\setbeamertemplate{frametitle}[myFrametitle]

\begin{document}

    \begin{frame}{Frame title}
        A frame
    \end{frame}

    \begin{frame}{Frame title}{with subtitle}
        Another frame
    \end{frame}

 \end{document}

And the result is the following.

Example

Axel Krypton
  • 1,083
  • IIRC, beamer doesn't add the frame title or compute the height of the colorbox until after everything else is done. The information simply isn't available when you want it. – John Kormylo Feb 15 '19 at 16:17
  • See also https://tex.stackexchange.com/questions/397523/tikz-based-beamer-frame/397557?r=SearchResults&s=1|23.6126#397557 – John Kormylo Feb 15 '19 at 16:33
  • @JohnKormylo I am not sure. If I define a new length and I set it in a redefinition of the beamercolorbox environment as I wrote in the first bullet above, I can use it after the environment. If you mean that I should compile twice the document to really have it available, well, this is completely fine for me. – Axel Krypton Feb 15 '19 at 16:37
  • @JohnKormylo I edited the question setting up a second MWE in which I did explicitly what I meant in the work-around proposed in the question. Maybe you were thinking to something else respect to what I meant. Sorry if I was unclear. I hope this add-on clarifies more. – Axel Krypton Feb 15 '19 at 17:08
  • This feels like an xy-problem. Maybe you could describe what "to be used to adapt dynamically stuff" will entail. Maybe there is another approach. – samcarter_is_at_topanswers.xyz Mar 08 '19 at 15:47
  • @samcarter I am asking exactly what I need: how to get the height of the last shipped out beamercolorbox in some form (e.g. a length), no Y for X trade. ;) Anyway, a possible use case is to vertically center a logo in a given beamercolorbox (not necessarily the frametitle box). And in general I am also just curious if there is a way to print the height in the slide as I did in a patchy way. Already simply using tikz alone would make life easier... – Axel Krypton Mar 11 '19 at 17:20
  • 1
    @AxelKrypton Still sounds very much like an xy-problem. Vertically centre an image in a beamercolorbox, I'd simply use columns or minipages and the image will automatically be centred correctly. Proof of concept: https://pastebin.com/bMFpcnsN – samcarter_is_at_topanswers.xyz Mar 11 '19 at 17:36
  • For printing the length in a less patchy way: I would use the printlen package. If you don't want to redefine the whole frametitle template, you could use \addtobeamertemplate{frametitle}{}{whatever you want to add} – samcarter_is_at_topanswers.xyz Mar 11 '19 at 17:40
  • @samcarter A columns environment inside a beamercolorbox is definitely a simple idea I could try to consider. Thanks! On the other hand, I do not see how to use the \addtobeamertemplate command to print/get the size of the last shipped out beamercolorbox, because I would need to query information from \beamer@tempbox (see the % <--- ADDED lines in my patchy code)... am I missing something here?! – Axel Krypton Mar 11 '19 at 19:05
  • @AxelKrypton See, a classic xy-problem :) The \addtobeamertemplate idea was just to avoid the redefinition of the frametitle, you would sill need to patch the colorbox itself. Also this could be simplified by actually patching the definition (e.g. with xpatch) instead of redefining it – samcarter_is_at_topanswers.xyz Mar 11 '19 at 19:53
  • I still don't know what exactly you are trying to do, maybe the columns are even overkill and simple minipages or even parboxes would do the job just fine. – samcarter_is_at_topanswers.xyz Mar 11 '19 at 19:57
  • @samcarter I still do not think it is a XY problem ;) I am actually more working on different layers on the same slide, i.e. knowing some lengths is useful not to touch what has already been done on the slide. Never mind... :) I think that you had another good point with xpatch, so if you write up an answer patching the beamercolorbox environment to in practice shorten what I did above copying its definition, then I would be happy to accept it! Do you see any side effect in this patch? One should patch the environment in 3 places (e.g. before each \box\beamer@tempbox%). – Axel Krypton Mar 11 '19 at 20:23
  • @AxelKrypton I guess I'll better leave xpatch to one of the xpatch experts. – samcarter_is_at_topanswers.xyz Mar 11 '19 at 23:50

1 Answers1

1

You can simplify your code by patching the beamercolorbox instead of redefining it:

\documentclass{beamer}
\usetheme{Madrid}

\makeatletter \newlength{@lastbeamercolorboxheight}

\patchcmd{\endbeamercolorbox}{ \box\beamer@tempbox% }{% \global\setlength{@lastbeamercolorboxheight}{\ht\beamer@tempbox}% \box\beamer@tempbox% }{}{}

\patchcmd{\endbeamercolorbox}{ \box\beamer@tempbox% }{% \global\setlength{@lastbeamercolorboxheight}{\ht\beamer@tempbox}% \box\beamer@tempbox% }{}{}

\patchcmd{\endbeamercolorbox}{ \box\beamer@tempbox% }{% \global\setlength{@lastbeamercolorboxheight}{\ht\beamer@tempbox}% \box\beamer@tempbox% }{}{}

\addtobeamertemplate{frametitle}{}{\textcolor{black}{\the@lastbeamercolorboxheight}} \makeatother

\begin{document}

\begin{frame}{Frame title}
    A frame
\end{frame}

\begin{frame}{Frame title}{with subtitle}
    Another frame
\end{frame}

\end{document}

  • Wow, you came back to my question more than 3 years later, respect! :) Reading again my dirty redefinition of the beamercolorbox environment, I now noticed that in the \if logic only one \box\beamer@tempbox will be hit and patching the end of the environment is simply brilliant. As you did, the replacement has to be done three times, hence the 3 identical \patchcmd. I guess using regexpatch package is here useful, taking advantage of \xpatchcmd* (it is then important to use a % at the end of the first line, \xpatchcmd*{\endbeamercolorbox}{%). – Axel Krypton Sep 23 '22 at 15:39