41

The beamer command \alt<*overlay specification*>{foo}{bar} will insert foo if the current slide is within the given overlay specification, and bar otherwise. It's equivalent to

\only<*overlay specification*>{foo}\only<*complementary overlay specification*>{bar}

Here is a sample document using it:

\documentclass{beamer}
\begin{document}
\begin{frame}
\begin{align*}
y &= \frac{(x^2+1)\sqrt{x+3}}{x-1} \\
\ln y &= \ln (x^2+1) + \frac{1}{2} \ln(x+3) - \ln(x-1) \\
\frac{1}{y} \frac{dy}{dx}
      &= \frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\end{align*}
So
\[
\begin{split}
\frac{dy}{dx} &= \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}
\end{split}
\]
\end{frame}
\end{document}

This is very useful and saves typing. But if one of the alternatives is larger than the other, it leads to jiggling slides.

What I would like is a overlay-specification-aware command \altvisible that would take the same amount of space on each slide. So it would have to insert a box as big as the bigger of the two, and then set the correct material. So the above document with \altvisible instead of \alt would not jiggle.

Any takers?

David Carlisle
  • 757,742
Matthew Leingang
  • 44,937
  • 14
  • 131
  • 195

5 Answers5

30

Following up the discussion of diabonas answer, here my suggestion. The idea to use phantom boxes seems the way to go. Here the two alternatives are boxed so that they can be measured. The code could be improved to detect the mode (text, math, display math, etc.) by itself and avoid the re-boxing which happens in the phantom commands.

\documentclass{beamer}
\newcommand<>\Alt[2]{{%
    \sbox0{$\displaystyle #1$}%
    \sbox1{$\displaystyle #2$}%
    \alt#3%
        {\rlap{\usebox0}\vphantom{\usebox1}\hphantom{\ifnum\wd0>\wd1 \usebox0\else\usebox1\fi}}%
        {\rlap{\usebox1}\vphantom{\usebox0}\hphantom{\ifnum\wd0>\wd1 \usebox0\else\usebox1\fi}}%
}}

\begin{document}
\begin{frame}
\begin{align*}
y &= \frac{(x^2+1)\sqrt{x+3}}{x-1} \\
\ln y &= \ln (x^2+1) + \frac{1}{2} \ln(x+3) - \ln(x-1) \\
\frac{1}{y} \frac{dy}{dx}
      &= \frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\end{align*}
So
\[
\begin{split}
\frac{dy}{dx} &= \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}
\end{split}
\]
\end{frame}
\end{document}

Here the version which checks the mode and math-style automatically. It works in all math and text modes:

\documentclass{beamer}

\makeatletter
% Detect mode. mathpalette is used to detect the used math style
\newcommand<>\Alt[2]{%
    \begingroup
    \ifmmode
        \expandafter\mathpalette
        \expandafter\math@Alt
    \else
        \expandafter\make@Alt
    \fi
    {{#1}{#2}{#3}}%
    \endgroup
}

% Un-brace the second argument (required because \mathpalette reads the three arguments as one
\newcommand\math@Alt[2]{\math@@Alt{#1}#2}

% Set the two arguments in boxes. The math style is given by #1. \m@th sets \mathsurround to 0.
\newcommand\math@@Alt[3]{%
    \setbox\z@ \hbox{$\m@th #1{#2}$}%
    \setbox\@ne\hbox{$\m@th #1{#3}$}%
    \@Alt
}

% Un-brace the argument
\newcommand\make@Alt[1]{\make@@Alt#1}

% Set the two arguments into normal boxes
\newcommand\make@@Alt[2]{%
    \sbox\z@ {#1}%
    \sbox\@ne{#2}%
    \@Alt
}

% Place one of the two boxes using \rlap and place a \phantom box with the maximum of the two boxes
\newcommand\@Alt[1]{%
    \alt#1%
        {\rlap{\usebox0}}%
        {\rlap{\usebox1}}%
    \setbox\tw@\null
    \ht\tw@\ifnum\ht\z@>\ht\@ne\ht\z@\else\ht\@ne\fi
    \dp\tw@\ifnum\dp\z@>\dp\@ne\dp\z@\else\dp\@ne\fi
    \wd\tw@\ifnum\wd\z@>\wd\@ne\wd\z@\else\wd\@ne\fi
    \box\tw@
}

\makeatother

\begin{document}

% Test the different modes and math styles
\begin{frame}
Display:
\[
\begin{split}
\frac{dy}{dx} &= \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}.
\end{split}
\]

In-Text:
\(
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}.
\)

Subscript:
\(
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}.
\)
\[
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}.
\]

Sub-Subscript:
\(
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}}.
\)
\[
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}}.
\]

Text-mode:
XXXX  \Alt<2>{aaaaa}{Ag}.


\end{frame}
\end{document}

The dots at the end are to visualize the constant width only.

Result

David Carlisle
  • 757,742
Martin Scharrer
  • 262,582
  • 1
    This looks excellent. I will test it on Monday. – Matthew Leingang Mar 19 '11 at 12:33
  • @Matthew: Did this work out for you? – Martin Scharrer May 10 '11 at 13:36
  • I did not get around to trying it. Next time... – Matthew Leingang May 10 '11 at 13:43
  • Just had cause to use this and it worked just fine (though I did change the \renewcommand\Alt to \newcommand\Alt at the start before testing it). – Andrew Stacey May 18 '11 at 11:13
  • @Andrew: Thanks. It should be \newcommand\Alt. The \renewcommand\Alt was still there from my experiment phase. – Martin Scharrer May 18 '11 at 22:00
  • 2
    I find myself using this again and thinking that it deserves more than a paltry three votes ... – Andrew Stacey Dec 02 '11 at 11:30
  • 2
    @AndrewStacey: I have to make a real package out of it and announce it here. Maybe then I get some more votes. – Martin Scharrer Dec 02 '11 at 11:43
  • 4
    Do that! I've already put it in a .sty file in my own texmf tree for easy use in various documents. I called it extalt, most likely for extended alt (I chose the name back in May so the actual reasons are - thankfully - lost in the mists (and midsts) of time). – Andrew Stacey Dec 02 '11 at 11:50
  • Thanks for these macros! They proved quite handy in a presentation I recently gave. I second the suggestion that you should package them. Also, if you do so, consider borrowing from my slightly revised version which supports Beamer's incremental overlay specifications. – bcat Mar 31 '12 at 20:59
  • @bcat I also second the suggestion that these macros should be incorporated into a package! I found it useful to add an optional argument to specify the position of the smaller text within the larger box. I added this small revision as an answer. It was my first attempt at writing a nontrivial macro involving plain TeX, so I would appreciate feedback on any corrections you might have. – Henry DeYoung Jul 16 '12 at 22:18
  • It seems to me that none of these work anymore; a simple fix is to change box0 to box2. Somehow, alt is messing with box0 nowadays. – Michaël May 04 '18 at 22:19
22

The second \Alt command in Martin Scharrer's answer is excellent; however, it can cause problems when used with Beamer's incremental overlay specifications (e.g., <+>). This is because Martin's implementation invokes the underlying \alt command using \mathpalette. \mathpalette internally uses \mathchoice, which actually typesets the contents given for each of the four math styles before deciding which to use in the final output.

Example of the issue

\documentclass{beamer}

% [Paste Martin's second set of `\Alt` macros here.]

\begin{document}

\begin{frame}
  \begin{itemize}
    \item Some stuff\dots

      \pause

    \item An equation I'll reveal in pieces:
      \[
        \frac{-4}{2} + \frac{9}{3} =
        \Alt<+>{
          \vcenter{\hbox{ ?\thinspace?}}
        }{
          \frac{-4 + 9}{2 + 3} = 1.
        }
      \]

      \pause

    \item Some more stuff\dots
  \end{itemize}
\end{frame}

\end{document}

Compiling this code yields a document with seven slides rather than the desired four, as TeX is executing the Beamer \alt macro---and thus incrementing the beamerpauses counter---four times rather than one.

Solving the problem

Move the \alt invocation outside of \mathpalette/\mathchoice. Unfortunately, this means that the new \Alt command's arguments will be typeset eight times (four for each of the \alt macro's two arguments) rather than four times as in Martin's code, but the added overhead seems necessary to make \Alt behave the same as \alt with respect to incremental overlays.

Also, it's a minor thing, but adding a \leavevmode before typesetting the \Alt content box seems to make the command behave a bit less surprisingly in some cases, e.g., at the start of a list item.

Revised code (supports incremental overlays)

\usepackage{etoolbox} % For `\ifbool`, `\ifnumcomp`.

\makeatletter
\newcommand*\Alt{\alt{\Alt@branch0}{\Alt@branch1}}

\newcommand\Alt@branch[3]{%
  \begingroup
  \ifbool{mmode}{%
    \mathchoice{%
      \Alt@math#1{\displaystyle}{#2}{#3}%
    }{%
      \Alt@math#1{\textstyle}{#2}{#3}%
    }{%
      \Alt@math#1{\scriptstyle}{#2}{#3}%
    }{%
      \Alt@math#1{\scriptscriptstyle}{#2}{#3}%
    }%
  }{%
    \sbox0{#2}%
    \sbox1{#3}%
    \Alt@typeset#1%
  }%
  \endgroup
}

\newcommand\Alt@math[4]{%
  \sbox0{$#2{#3}\m@th$}%
  \sbox1{$#2{#4}\m@th$}%
  \Alt@typeset#1%
}

\newcommand\Alt@typeset[1]{%
  \ifnumcomp{\wd0}{>}{\wd1}{%
    \def\setwider   ##1##2{##2##1##2 0}%
    \def\setnarrower##1##2{##2##1##2 1}%
  }{%
    \def\setwider   ##1##2{##2##1##2 1}%
    \def\setnarrower##1##2{##2##1##2 0}%
  }%
  \sbox2{}%
  \sbox3{}%
  \setwider2{\wd}%
  \setwider2{\ht}%
  \setwider2{\dp}%
  \setnarrower3{\ht}%
  \setnarrower3{\dp}%
  \leavevmode
  \rlap{\usebox#1}%
  \usebox2%
  \usebox3%
}
\makeatother
bcat
  • 321
12

As a further revision to Martin Scharrer's and bcat's nifty answers, here is a version that allows one to specify the position (l, c, or r) of the smaller text within the larger box:

\usepackage{etoolbox}
\usepackage{mathtools}

\makeatletter
% Detect mode. mathpalette is used to detect the used math style
\newcommand<>\Alt[3][l]{%
  \begingroup
    \providetoggle{Alt@before}%
    \alt#4{\toggletrue{Alt@before}}{\togglefalse{Alt@before}}%
    \ifbool{mmode}{%
      \expandafter\mathpalette
      \expandafter\math@Alt
    }{%
      \expandafter\make@Alt
    }%
    {{#1}{#2}{#3}}%
  \endgroup
}

% Un-brace the second argument (required because \mathpalette reads the three arguments as one
\newcommand\math@Alt[2]{\math@@Alt{#1}#2}

% Set the two arguments in boxes. The math style is given by #1. \m@th sets \mathsurround to 0.
\newcommand\math@@Alt[4]{%
  \setbox\z@ \hbox{$\m@th #1{#3}$}%
  \setbox\@ne\hbox{$\m@th #1{#4}$}%
  \@Alt{#2}%
}

% Un-brace the argument
\newcommand\make@Alt[1]{\make@@Alt#1}

% Set the two arguments into normal boxes
\newcommand\make@@Alt[3]{%
  \sbox\z@ {#2}%
  \sbox\@ne{#3}%
  \@Alt{#1}%
}

% Place one of the two boxes using \rlap and place a \phantom box with the maximum of the two boxes
\newcommand\@Alt[1]{%
  \setbox\tw@\null
  \ht\tw@\ifnum\ht\z@>\ht\@ne\ht\z@\else\ht\@ne\fi
  \dp\tw@\ifnum\dp\z@>\dp\@ne\dp\z@\else\dp\@ne\fi
  \wd\tw@\ifnum\wd\z@>\wd\@ne\dimexpr\wd\z@/2\relax\else\dimexpr\wd\@ne/2\relax\fi
  %
  \ifstrequal{#1}{l}{%
    \rlap{\iftoggle{Alt@before}{\usebox\z@}{\usebox\@ne}}%
    \copy\tw@
    \box\tw@
  }{%
    \ifstrequal{#1}{c}{%
      \copy\tw@
      \clap{\iftoggle{Alt@before}{\usebox\z@}{\usebox\@ne}}%
      \box\tw@
    }{%
      \ifstrequal{#1}{r}{%
        \copy\tw@
        \box\tw@
        \llap{\iftoggle{Alt@before}{\usebox\z@}{\usebox\@ne}}%
      }{%
      }%
    }%
  }%
}
\makeatother

Unlike bcat's macro, I use \mathpalette and avoid the bug in Martin's answer by toggling a flag inside \alt; the flag says whether the first or second argument of \Alt should be set.

This was my first attempt at writing a nontrivial macro involving plain TeX, so please let me know if there are better style practices I should use.

  • Scratch registers with an odd index (\box1, for instance) should be used only for global assignments. This is a recommended practice, not following it might lead to memory problems. – egreg Jul 16 '12 at 22:28
  • @egreg So I should not be using \box\@ne in this case? Should I prefer the LaTeX style of \newsavebox etc. here, or is there a reason to stick to the plain TeX primitives for this macro? Thanks in advance for your help! – Henry DeYoung Jul 17 '12 at 17:06
  • You can use \box\tw@ or \box4, \box6, \box8; in this case the scratch boxes seem to be correctly used. When in doubt allocate new ones. – egreg Jul 17 '12 at 18:27
  • 2
    Could you consider giving an example of how your new macro with the additional parameter is supposed to be used? – a3nm Aug 26 '18 at 15:26
4

Maybe \vphantom could help? The following code defines a macro \altvphantom which uses the normal \alt command and additionally inserts \vphantom{<first argument>} and \vphantom{<second argument>} to ensure a consistent vertical spacing and to prevent the jiggling:

\newcommand<>{\altvphantom}[2]{\alt#3{#1}{#2}\vphantom{#1}\vphantom{#2}}

Just add this line to your document and replace \alt<2>{...}{...} with \altvphantom{...}{...}, and the preceding lines will stop moving around.

UPDATE: If you do not want the last line to move left and right either, you can say \usepackage{mathtools} in the preamble and change the "\alt-line" to

\altvphantom<2>{\mathrlap{\frac{(x^2+1)\sqrt{x+3}}{x-1}}}{\mathrlap{y}}

Another possibilty would be to use the fleqn option: If you use \documentclass[fleqn]{beamer}, all the equations will flush left, which solves the problem with the moving line, too.

diabonas
  • 25,784
1

Here is the definition of alt in beamerbaseoverlay.sty:

\def\alt{\@ifnextchar<{\beamer@alt}{\beamer@alttwo}}
\long\def\beamer@alttwo#1#2{\beamer@ifnextcharospec{\beamer@altget{#1}{#2}}{#1}}
\long\def\beamer@altget#1#2<#3>{%
  \def\beamer@doifnotinframe{#2}\def\beamer@doifinframe{#1}%
  {\beamer@masterdecode{#3}}\beamer@donow}
\long\def\beamer@alt<#1>#2#3{%
  \def\beamer@doifnotinframe{#3}\def\beamer@doifinframe{#2}%
  {\beamer@masterdecode{#1}}\beamer@donow}

Weirdly, \uncover is defined in terms of \alt:

\newcommand{\uncover}{\alt{\beamer@fakeinvisible}{\beamer@makecovered}}

So I suspect that putting \beamer@fakeinvisible and \beamer@makeuncovered at some point in a definition like that of \alt should work? But I couldn't work out where exactly to fit it in.

Seamus
  • 73,242