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

13
  • 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. Feb 15, 2019 at 16:17
  • @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. Feb 15, 2019 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. Feb 15, 2019 at 17:08
  • 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: pastebin.com/bMFpcnsN Mar 11, 2019 at 17:36

1 Answer 1

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}
1
  • 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}{%). Sep 23, 2022 at 15:39

You must log in to answer this question.

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