When writing Beamer slides for a talk, I find that I often want to have some text (usually math) visible on a group of slides and be alerted on only the first slide in the group. For example, I can achieve this with $1 + 1 = \action<visible@+- | alert@+>{2}$
, $2 + 2 = \action<visible@.(-1)- | alert@.(-1)>{4}$
, etc.
However, it would be more maintainable and readable if these actions could be combined into one alertvisible
action. Using a simple macro, it's easy to handle the case where the group of slides has the form <slide number>-
:
\documentclass{beamer}
\newcommand{\alertvisible}[1]{visible@#1- | alert@#1}
\begin{document}
\begin{frame}{Frame Title}
Facts:\pause
\begin{itemize}
\item $1 + 1 = \action<visible@+- | alert@+>{2}$
\item $2 + 2 = \action<\alertvisible{+}>{4}$
\item $3 + 3 = \action<\alertvisible{.(-1)}>{6}$
\item $4 + 4 = \action<\alertvisible{+}>{8}$
\end{itemize}
\end{frame}
\end{document}
Unfortunately, this simple solution does not handle the general case. For example, I might want to have a single alertvisible
action so that \action<alertvisible@+,+(2)-+(3)>
is equivalent to \action<visible@+,+(2)-+(3) | alert@+>
. This would seem to require a way, say \firstslideof
, to extract the logically first slide in a group so that one could define
\newenvironment<>{alertvisibleenv}%
{\begin{visibleenv}#1\begin{alertenv}\firstslideof{#1}}%
{\end{alertenv}\end{visibleenv}}
Is there some way to define \firstslideof
to allow this sort of general definition of alertvisibleenv
?
Edit:
I am attempting to use the xparse
and xstring
packages to build \firstslideof
, which, for some reason, I decided to rename as \extract
. The basic idea is to parse out the left endpoint of each range in the slide specification and evaluate it to a numeric value. The slide specification with the smallest value should be returned as the "first-slide-of". For example, \extract<+-+(1), 3, +, +(-2)-8>
should return +(-2)
when the value of beamerpauses
is smaller than 5, and 3
otherwise.
Here is the code that I have so far:
\documentclass{beamer}
\usepackage{etoolbox}
\usepackage{etextools}
\usepackage{xparse}
\usepackage{xstring}
\makeatletter
\NewDocumentCommand{\extract}{>{\SplitList{,}}r<>}{%
% Initially set the minimum slide spec to something large
\def\beamer@extract@min{+(1000)}%
\ProcessList{#1}{\beamer@extract}%
}
\newcommand*{\beamer@extract}[1]{%
% Substitute \beamer@extract@groupplus for each +
\saveexpandmode
\expandarg
\StrSubstitute{#1}{+}{\noexpand\beamer@extract@groupplus}[\@tmpa]%
\restoreexpandmode
%
% Once \@tmpa is fully expanded, each occurrence of
% a possibly shifted + will be surrounded with { }.
% The { } are used to prevent \beamer@@extract from
% incorrectly splitting +(-1) as +( and 1).
%
%% How do I fully expand \@tmpa here? Is this right?
\ExpandNext{\beamer@@extract}{\@tmpa}%
}
\NewDocumentCommand{\beamer@@extract}{>{\SplitArgument{1}{-}}m}{%
% Use \SplitArgument and \@firstoftwo to get left endpoint
% of range.
Found: `\@firstoftwo#1'\par
%
% Evaluate the prospective and current minima to
% a numeric value and store the result in \@tmpa
% and \@tmpb, respectively.
\beamer@@extract@eval{\@tmpa}{\@firstoftwo#1}%
\beamer@@extract@eval{\@tmpb}{\beamer@extract@min}%
%
% If the prospective minimum is smaller, then
% make it the current minimum.
\ifnumless{\@tmpa}{\@tmpb}{%
\expandnext{\def\beamer@extract@min}{\@firstoftwo#1}%
Updated
}{%
Not Updated
}%
Minimum: \meaning\beamer@extract@min\par
}
\newcommand*{\beamer@@extract@eval}[2]{%
% Substitute \beamer@@extract@evalplus for each occurrence of
% + in #2, storing the fully expanded result in #1.
\saveexpandmode
\expandarg
\StrSubstitute{#2}{+}{\noexpand\beamer@@extract@evalplus}[#1]%
\restoreexpandmode
%% How do I fully expand #1 here?
}
\NewDocumentCommand{\beamer@@extract@evalplus}{r()}{%
\value{beamerpauses}+#1%
}
\NewDocumentCommand{\beamer@extract@groupplus}{D(){0}}{%
{+(#1)}%
}
\makeatother
\begin{document}
\begin{frame}{Test Frame}
% The following should set \beamer@extract@min to
% +(-2) if beamerpauses < 5
% 3 if beamerpauses >= 5
\extract<+-+(1), 3, +, +(-2)-8>
\end{frame}
\end{document}
Unfortunately, when I run the code, I get an error: You can't use \numexpr in horizontal mode. \ifnumcomp ...\ifnum \numexpr #1\relax #2\numexpr #3\relax \expandafter \@fi...
. My guess is that this error comes from expansion problems at the lines marked above with %%
.
Question:
Is the problem indeed related to expansion? If so, how can I fix it?
\only
,\temporal
, etc., that can select the first slide in a range, one would have to mimic the structure of the overlay specification parser inbeamerbasedecode.sty
. Which looks complicated...\only
,\temporal
, etc. that works. Instead of mimickingbeamerbasedecode.sty
, I wonder if there is some way to leverage thexparse
package to make things (partly) easier.visible
action works by displacing its contents a large amount for slides where the contents should be invisible. This means that it is still processed making it a little tricky to determine the point at which it is actually printed on the page.alertvisible
", I don't think I need that generality; isn't it enough to expand+
s and.
s and find the smallest slide in that set? (I've been working on code along this line, now that I've learned more about TeX programming. I'm running into expansion bugs; after I try some more, I will post an edit or separate question.) Maybe I'm missing your point, though?