[Tex/LaTex] How to write overlay-specification–aware macros that work w/o the beamer class

beamerbeamerarticlebest practicesmacrospackage-writing

The LaTeX beamer class provides with \newcommand<> and \newenvironment<> an interface to define own commands that can deal with beamer's overlay specifications. Internally, commands defined this way then typically employ other beamer-provided overlay-aware commands, such as \only<> or \onslide<>, to achieve the desired effect.

However, how does one write a package that provides its commands overlay-aware if used together with beamer, but can also be used with other document classes?

The brute force solution would be to define every command twice, depending of it being used within beamer or not:

\@ifclassloaded{beamer}{%
  % beamer
  \newcommand<>{\foo}[1]{\only#2{...}}
  \newcommand<>{\bar}[1]{... \onslide#2{...}}
}{%
  % other class
  \newcommand{\foo}[1]{...}
  \newcommand{\bar}[1]{...}
}

However, I would prefer an approach that just mimics a beamer-like interface, so that the actual macro definitions can be kept as is:

\@ifclassloaded{beamer}{%
  % beamer ==> do nothing
}{%
  % other class ==> provide beamer command mockups
  \def\newcommand ...
  \def\onslide ...
  \def\only ...
}

\newcommand<>{\foo}[1]{\only#2{...}}
\newcommand<>{\bar}[1]{... \onslide#2{...}}

There is the beamerarticle package that is intended to provide these commands for beamer's article mode (used in the MWE below). However, beamerarticle goes way to far, as it also defines commands, such as \frame and \note and patches, as beamer does, basically all of the standard commands for sectioning and lists, which renders it incomapatible to many other packages and document classes. I am looking for a more light-weight, less invasive solution.

I also tried to load the beamerbaseoverlay sub-package alone, which apparently provides the definitions of \newcommand<>, \only<> and so on. However, this package is not intended to be used alone; I tried to hunt down its dependencies, but gave it up after loading half a dozen of extra packages without success.

Any other ideas? Any package I did overlook?

MWE with some real code from the package I am currently writing to play around:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{listings}

% BEGIN package content
\makeatletter

  \@ifclassloaded{beamer}{}{
    \RequirePackage{beamerarticle} % <-- I would like to avoid this!
  }  
  \RequirePackage{tikz}

  % uses beamer's \only<> 
  \tikzset{onslide/.code args={<#1>#2}{%
    \only<#1>{\pgfkeysalso{#2}} 
  }}

  % uses beamer's \newenvironment<>, onlyenv
  \newenvironment<>{btHighlight}[1][]
  {\begin{onlyenv}#2\begingroup\tikzset{bt@Highlight@par/.style={#1}}\begin{lrbox}{\@tempboxa}}
  {\end{lrbox}\bt@HL@box[bt@Highlight@par]{\@tempboxa}\endgroup\end{onlyenv}}

  % uses beamer's \newcommand<>, \only<>
  \newcommand<>\btHL[1][]{%
    \only#2{\begin{btHighlight}[#1]\bgroup\aftergroup\bt@HL@endenv}%
  }
  \def\bt@HL@endenv{%
    \end{btHighlight}%   
    \egroup
  }
  \newcommand{\bt@HL@box}[2][]{%
    \begin{tikzpicture}[remember picture]%
      \pgfpathrectangle{\pgfpoint{1pt}{0pt}}{\pgfpoint{\wd #2}{\ht #2}}%
      \pgfusepath{use as bounding box}%
      \node[anchor=base west,  fill=orange!30,outer sep=0pt,inner xsep=1pt, inner ysep=0pt, rounded corners=3pt, minimum height=\ht\strutbox+1pt,#1]{\raisebox{1pt}{\strut}\strut\usebox{#2}};
    \end{tikzpicture}
  }

\makeatother
% END package content

\begin{document}

  \begin{lstlisting}[%
      language=C, numbers=left, gobble=4,
      moredelim={**[is][{\btHL[pin=30:The applications entry point, onslide=<2->{fill=red!50}]}]{@}{@}}%
    ]
    @int main (void)@ { 
      printf("Hello World!"); return 0;
    } 
  \end{lstlisting}

\end{document}

enter image description here

Best Answer

According to the beamer manual, a command declared with \newcommand<> has access to an extra argument containing the overlay specification; thus, as a general principle, it is not possible to write an overlay-aware command that also functions without change without beamer.

However, one can go the other way, and beamer provides a ready-made way of doing this:

\newcommand\foo[2][]{...}
\renewcommand<>\foo[2][]{...\beameroriginal\foo...}

Inside the renewed \foo, you can use \beameroriginal\foo to call on the original definition, even to the extent of adding overlay-aware commands that build on it. Put that inside an \@ifclassloaded and you're good to go.

It would be really convenient if \renewcommand itself had this feature. But the fact that it doesn't means that the analogy

\renewcommand<>:\newcommand<>::\renewcommand:\newcommand

fails; it is not the "overlay aware" version of \renewcommand, and it is not intended for renewing commands created with \newcommand<>; instead, it is intended for renewing commands created with \newcommand to behave as though they were created with \newcommand<>.