3

Trying to answer myself a question asked in my comment to this answer, I ended up with the code below based on the Ulrike's one but which make the \balloon macro overlay aware. But, when used before listings, this macro introduces a spurious vertical space. Do you see where it can come from?

Update

The code has been slightly changed and an animation has been added in order to make the spurious vertical space more noticeable.

Update 2

In fact, in order to avoid troubles due to duplicates when making \balloon overlays aware, it is necessary for the listings to have distinct names. Using for this the slides numbers (\the\beamer@slideinframe) is enough within a given frame, but not enough through the whole document. Adding the frames numbers (\insertframenumber) does the trick.

\documentclass{beamer}
%
\setbeamertemplate{footline}[page number]
%
\usepackage{listings}
\usepackage[foreground]{pagegrid}
\usepackage{tikz}
\usetikzlibrary{tikzmark,fit,calc}
\usetikzmarklibrary{listings}
\lstset{basicstyle=\ttfamily}
\tikzset{%
  balloon/.style={%
    draw,%
    fill=blue!20,%
    opacity=0.4,%
    inner sep=4pt,%
    rounded corners=2pt%
  },%
}
%
\makeatletter
\renewcommand\iftikzmark[3]{%
  \@ifundefined{save@pt@#1-\the\beamer@slideinframe}{%
    #3%
  }{%
    #2%
  }%
}%
\newcommand{\@balloon}[4]{%
  \pgfmathtruncatemacro\@firstline{%
    #3-1%
  }%
  \iftikzmark{line-#2-\@firstline-start}{%
    \iftikzmark{line-#2-#3-first}{%
      \xdef\@blines{%
        ($ ({pic cs:line-#2-\@firstline-start} -| {pic
          cs:line-#2-#3-first})!.5!({pic cs:line-#2-#3-first}) $)%
      }%
    }{%
      \iftikzmark{line-#2-#3-start}{%
        \xdef\@blines{%
          ({pic cs:line-#2-\@firstline-start} -| {pic cs:line-#2-#3-start})%
        }%
      }{%
        \xdef\@blines{(pic cs:line-#2-\@firstline-start)}%
      }%
    }%
  }{%
    \xdef\@blines{}%
  }%
  \foreach \k in {#3,...,#4} {%
    \iftikzmark{line-#2-\k-first}{%
      \xdef\@blines{%
        \@blines (pic cs:line-#2-\k-first)%
      }%
    }{}%
    \iftikzmark{line-#2-\k-end}{%
      \xdef\@blines{%
        \@blines (pic cs:line-#2-\k-end)%
      }%
    }{}%
  }%
  \ifx\@blines\empty%
  \else%
  \edef\temp{\noexpand\tikz[remember picture,overlay]%
    \noexpand\node[fit={\@blines},balloon] (#1) {};}%
  \temp%
  \fi%
}
%
\newcommand<>{\balloon}[4][code\insertframenumber\the\beamer@slideinframe]{%
  \only#5{\@balloon{#2}{#1}{#3}{#4}}%
}
%
\lstnewenvironment{hllisting}[1][]{%
  \lstset{name=code\insertframenumber\the\beamer@slideinframe,#1}%
}{%
  \lstset{name=}%
}
\makeatother
%
\begin{document}
\begin{frame}[fragile]
\balloon<3>{comment}{5}{6}%
\balloon<4>{comment}{1}{3}%
\begin{hllisting}
1st line of code
2nd line of code
3rd line of code
4th line of code
5th line of code
6th line of code
\end{hllisting}
\end{frame}
\end{document}

enter image description here

  • @DavidCarlisle Sorry, I don't see your point: without the two \balloon lines, the listing has indeed the same vertical position as the listings of the slides 1 and 2 of my example (where the \balloon commands are ineffective) but is higher located than the ones of the slides 3 and 4 (where the \balloon commands are effective). I'll update my question to make it more visible. – Denis Bitouzé Jan 31 '17 at 21:25
5

There is no magic here: You are inserting a \tikz[overlay] in some frames and not in the others. A \tikz[overlay] may have no size but it is nevertheless a box and like an \mbox{} it can introduce spaces. This means that it is important to either use the \tikz[overlay] in places where such a box doesn't do harm. Or to use a similar box in all frames, e.g. by using as suggested by David an \mbox or e.g. \alt instead of \only in the definition of \balloon:

\documentclass{beamer}
%
\setbeamertemplate{footline}[page number]
%
\usepackage{listings}
\usepackage[foreground]{pagegrid}
\usepackage{tikz}
\usetikzlibrary{tikzmark,fit,calc}
\usetikzmarklibrary{listings}
\lstset{basicstyle=\ttfamily}
\tikzset{%
  balloon/.style={%
    draw,%
    fill=blue!20,%
    opacity=0.4,%
    inner sep=4pt,%
    rounded corners=2pt%
  },%
}
%
\makeatletter
\renewcommand\iftikzmark[3]{%
  \@ifundefined{save@pt@#1-\the\beamer@slideinframe}{%
    #3%
  }{%
    #2%
  }%
}%
\newcommand{\@balloon}[4]{%
  \pgfmathtruncatemacro\@firstline{%
    #3-1%
  }%
  \iftikzmark{line-#2-\@firstline-start}{%
    \iftikzmark{line-#2-#3-first}{%
      \xdef\@blines{%
        ($ ({pic cs:line-#2-\@firstline-start} -| {pic
          cs:line-#2-#3-first})!.5!({pic cs:line-#2-#3-first}) $)%
      }%
    }{%
      \iftikzmark{line-#2-#3-start}{%
        \xdef\@blines{%
          ({pic cs:line-#2-\@firstline-start} -| {pic cs:line-#2-#3-start})%
        }%
      }{%
        \xdef\@blines{(pic cs:line-#2-\@firstline-start)}%
      }%
    }%
  }{%
    \xdef\@blines{}%
  }%
  \foreach \k in {#3,...,#4} {%
    \iftikzmark{line-#2-\k-first}{%
      \xdef\@blines{%
        \@blines (pic cs:line-#2-\k-first)%
      }%
    }{}%
    \iftikzmark{line-#2-\k-end}{%
      \xdef\@blines{%
        \@blines (pic cs:line-#2-\k-end)%
      }%
    }{}%
  }%
  \ifx\@blines\empty%
  \else%
  \edef\temp{\noexpand\tikz[remember picture,overlay]%
    \noexpand\node[fit={\@blines},balloon] (#1) {};}%
  \temp%
  \fi%
}
%
\newcommand<>{\balloon}[4][code\the\beamer@slideinframe]{%
  \alt#5{\@balloon{#2}{#1}{#3}{#4}}{\tikz[overlay,remember picture]\path (0,0);}%
}
%
\lstnewenvironment{hllisting}[1][]{%
  \lstset{name=code\the\beamer@slideinframe,#1}%
}{%
  \lstset{name=}%
}
\makeatother
%
\begin{document}
\begin{frame}
\tikz[overlay]\node{a};

blblb
\end{frame}

\begin{frame}

blblb
\end{frame}



\begin{frame}[fragile]
\balloon<3>{comment}{5}{6}%
\balloon<4>{comment}{1}{3}%
\begin{hllisting}
1st line of code
2nd line of code
3rd line of code
4th line of code
5th line of code
6th line of code
\end{hllisting}
\end{frame}
\end{document}
  • \alt is a nice alternative! – Denis Bitouzé Feb 2 '17 at 9:19
  • 1
    BTw: You probably still can get different spaces if the balloon uses non existing "coordinates" and \@blines is empty. To be on the safe size you could an an empty picture after \ifx\@blines\empty. – Ulrike Fischer Feb 2 '17 at 9:21
  • Do you mean inserting \tikz[overlay,remember picture]\path (0,0); between the (currently consecutive) lines \ifx\@blines\empty% and \else%? – Denis Bitouzé Feb 2 '17 at 9:53
  • 1
    Yes. The issue can be seen if you have e.g. only \balloon<4>{comment}{10}{30}% in the frame. Be also aware, that every \balloon inserts a box. So spaces and empty lines between the commands can matter. – Ulrike Fischer Feb 2 '17 at 10:21
  • 1
    There is a spurious space at the end of the \alt. I corrected it. – Ulrike Fischer Feb 2 '17 at 13:51
5

Strangely enough I haven't fully traced exactly what beamer and tikzmark are doing here, but adding invisible whatsits in vertical mode, whether it's \color or \label or \tikzmark is always tricky and has difficult to control side effects. The solution in all cases is to get safely into horizontal mode before doing anything tricky.

If you do

\mbox{\balloon<3>{comment}{5}{6}%
\balloon<4>{comment}{1}{3}}%

then the listing appears at the same position on all four slides. The empty mbox makes a spurious paragraph that moves things down a bit, but as it's a fixed point and the same on all slides you can use a blank line and a negative vspace after the box if needed.

  • Maybe I accepted your answer too quickly: as you said, it provides a (nice!) workaround but doesn't answer the question "Do you see where it can come from?". Therefore, I'll "unaccecpt" it with the hope to draw attention of people (including you) who will be kind enough to think about it. I hope (but guess) you won't hate me for that ;) – Denis Bitouzé Feb 1 '17 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.