Beamer alt command like visible instead of like only
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.
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
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.