12

Using the following code I could put the ToC lines in the center in beamer:

\begin{frame}
  \begin{center}
  \tableofcontents
  \end{center}
\end{frame}

But how to make these ToC lines left aligned and still in the center?

Werner
  • 603,163
Z.H.
  • 3,346
  • 21
  • 39
  • 2
    You can only put the \tableofcontents inside a minipage-environment which can be centered via \centering. – Marco Daniel Dec 10 '11 at 17:12
  • The question http://tex.stackexchange.com/questions/47702/positioning-table-of-contents contains a fairly simple solution (manually calculated though) – naught101 May 02 '12 at 00:27

3 Answers3

8

A small calculation could advance the margins sufficiently to obtain the centered & left-aligned \tableofcontents display.

enter image description here

\documentclass{beamer}% http://ctan.org/pkg/beamer
\newsavebox{\longestsec}% Box to save longest sectional heading
\begin{document}

\begin{frame}
  \frametitle{Table of contents}
  \begin{lrbox}{\longestsec}Last section in the presentation\end{lrbox}% Capture longest title
  \setlength{\leftskip}{\dimexpr.5\textwidth-.5\wd\longestsec\relax}% Advance left margin accordingly
  \tableofcontents
\end{frame}

\section{First section}
​\begin{frame}
  \frametitle{First slide}
  This is a slide.
\end{frame}

\section{Last section in the presentation}% Longest sectional title
\begin{frame}
  \frametitle{Another slide}
  This is another slide
\end{frame}
\end{document}

​The reason for this round-about way is because beamer collapses the spacing when putting \tableofcontents within a minipage or varwidth (from the varwidth package).


Here is an automated way of capturing the width of the longest section:

\documentclass{beamer}% http://ctan.org/pkg/beamer
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\makeatletter
\newlength{\secnamelength}
\newsavebox{\longestsec}% Box to save longest sectional heading
\patchcmd{\beamer@section}% <cmd>
  {\beamer@savemode}% <search>
  {\begin{lrbox}{\longestsec}#1\end{lrbox}%
   \ifdim\wd\longestsec>\secnamelength\relax\setlength{\secnamelength}{\wd\longestsec}\fi%
   \beamer@savemode}% <replace>
  {}{}% <success><failure>
\AtEndDocument{% http://tex.stackexchange.com/q/137495/5764
  \immediate\write\@auxout{\global\secnamelength=\the\secnamelength}%
}
\makeatother
\begin{document}

\begin{frame}
  \frametitle{Table of contents}
  \setlength{\leftskip}{\dimexpr.5\textwidth-.5\secnamelength\relax}% Advance left margin accordingly
  \tableofcontents
\end{frame}

\section{First section}
​\begin{frame}
  \frametitle{First slide}
  This is a slide.
\end{frame}

\section{Last section in the presentation}% Longest sectional title
\begin{frame}
  \frametitle{Another slide}
  This is another slide
\end{frame}
\end{document}

The above stores the length of the longest section in \secnamelength that is written to the .aux at the end of the document. A patch of \beamer@section (with the aid of etoolbox is necessary to grab the maximum length of a section name and drop it into the .aux \AtEndDocument so it can be retrieved in a successive run.

Werner
  • 603,163
  • 1
    Great. But is it possible to automatically find out the longest sectional title? – Z.H. Dec 16 '11 at 12:07
  • Is this still a valid solution? Doesn't work in my texlive 2012 ... – BandGap Oct 11 '13 at 16:12
  • 1
    @Z.H.: A little late, but added the automation procedure. – Werner Oct 12 '13 at 18:05
  • @BandGap: Yes, and I've just added some automation. What is the problem you're experiencing with the above MWE? – Werner Oct 12 '13 at 18:06
  • It seems like the selection of a theme for beamer overwrites the placement of the ToC... If I comment the relevant line the ToC appears centered. – BandGap Oct 14 '13 at 11:35
  • When reading your answer to another question, I'm wondering if using varwidth inside center environment is possible for beamer TOC? – Z.H. Apr 07 '15 at 10:23
  • @Werner did you test this procedure when the longest section is the first section? I took the code you used in the "automated way" but unfortunatelly it always "oversees" the first section in the toc. Any idea why? – Ktree Jun 06 '17 at 13:39
  • @Werner, I found out that the problem was simply that my section had a short name \section[shortName]{longName} and of course your code will use the parameter #1. Can you provide a way to avoid this and always take the mandatory argument? – Ktree Jun 07 '17 at 11:50
  • @Ktree: Try this paste and let me know. If redefined \section to take only two arguments (first is optional, as usual). However, it still considers the optional argument #1 to be what ends up in the ToC. You can change it to use #2, but I'm not sure what the purpose of specifying \section[shortName]{longName} is then. – Werner Jun 07 '17 at 16:43
  • @werner well, the toc might not be the only place where the short version is used. I use them in the sidebar to show the current (sub)section. I have not tried your paste but I noticed that the toc is not centered if one uses an enumeration instead of a simple itemized list due to its itemsep and labelwidth. Thus I have to use this: \setlength{\leftskip}{\dimexpr.5\textwidth-.5\secnamelength+.5\labelsep-\labelwidth\relax} – stefanct Oct 09 '17 at 13:34
2

By patching \beamer@sectionintoc command, I could use varwidth inside center environment to make ToC lines left aligned and in the center.

The only limitation of this solution is, ToC lines could not be wrapped.

\documentclass{beamer}    
\usepackage{varwidth}    
\usepackage{etoolbox}
\makeatletter
%\patchcmd{\beamer@sectionintoc}{\vskip1.5em}{\vskip0.5em}{}{}
\patchcmd{\beamer@sectionintoc}{%
  \hbox{\vbox{%
    \def\beamer@breakhere{\\}%
    \beamer@tocact{\ifnum\c@section=#1\beamer@toc@cs\else\beamer@toc@os\fi}{section in toc}}}%
}{%
  \hbox{%
    \def\beamer@breakhere{}%
    \beamer@tocact{\ifnum\c@section=#1\beamer@toc@cs\else\beamer@toc@os\fi}{section in toc}}%
}{}{}
\makeatother    
\setbeamertemplate{section in toc}[sections numbered]    
\begin{document}

\begin{frame}
\frametitle{Table of contents}
\begin{center}
\begin{varwidth}{\textwidth}
\tableofcontents[sectionstyle=show,subsectionstyle=hide] 
\end{varwidth}
\end{center}
\end{frame}

\section{First section}    
​\begin{frame}
\frametitle{First slide}
This is a slide.
\end{frame}

\section{Second section in the presentation}    
\begin{frame}
\frametitle{Another slide}
This is another slide.
\end{frame}

\section{Last section}    
\begin{frame}
\frametitle{Last slide}
This is the last slide.
\end{frame}

\end{document}

enter image description here

Z.H.
  • 3,346
  • 21
  • 39
  • 1
    Apparently in my installed version this leads to squeezed sections, i.e. there is no vertical space between them. Any idea how to get the \vfills between the sections back? For some reason they are gone after using the patch. I have tried adding it back manually by inserting it into the second clause of the patch etc. but that apparently makes either the patch not applying (is there no warning?) or it does not work. – stefanct Sep 05 '16 at 09:48
  • Whatever it is... the alternative solution proposed by @Werner works fine. – stefanct Sep 08 '16 at 12:24
2

As shown in my comment, the automatism provided by Werner can be problematic if you have short names like \section[shortName]{longName} e.g. for beamer slide headlines. It always takes the first argument, which would then be the short name.

I implemented a quick solution now (with etoolbox and ifthen), which will take the second argument if provided:

\makeatletter
\newlength{\secnamelength}
\newsavebox{\longestsec}% Box to save longest sectional heading
\patchcmd{\beamer@section}% <cmd>
{\beamer@savemode}% <search>
{
\ifstrempty{#2}{%
    \begin{lrbox}{\longestsec}#1\end{lrbox}
    \ifthenelse{
        \wd\longestsec > \secnamelength
    }{
        \setlength{\secnamelength}{\wd\longestsec}
    }{}%else
}{
    \begin{lrbox}{\longestsec}#2\end{lrbox}
    \ifthenelse{
        \wd\longestsec > \secnamelength
    }{
        \setlength{\secnamelength}{\wd\longestsec}
    }{}%else
}
\beamer@savemode
}% <replace>
{}{}% <success><failure>
\AtEndDocument{% http://tex.stackexchange.com/q/137495/5764
    \immediate\write\@auxout{\global\secnamelength=\the\secnamelength}%
}
\makeatother
Ktree
  • 688