I try to present some code by using beamer. The problem I face is that most of the times the code does not fit into a single slide. Changing font size is not a solution because the audience in the back will have a hard time to read my slides.
So, I decided to create an environment where I can put my code, and pass as arguments
- The first (start) line of the part of the code I want to present.
- The last (end) line of the part of the code I want to present.
- The maximum number of lines each slide may have.
The environment should produce one frame with as many slides as necessary to show all the lines of the code I choose (from start to end line). The scroll could be downwards or upwards based on an optional step argument (e.g +-1).
I divide the problem into two sub problems:
- A) The first one is given a number of lines, I want to create one slide that shows a part/range of those lines.
- B) The second sub problem is given a maximum number of lines per slide, and a part/range of lines (larger than the maximum), I want to compute the number of slides that are necessary in order to show all the lines in the range without exceeding the maximum lines per slide and the ranges of each sequential slide.
When I have both solutions I will be able to combine them and create the scrolling environment I want (hopefully...).
My solution to the first sub-problem was the following.
listings package has a firstline and lastline options. So I created a 'codepart' environment that takes as arguments the first and the last lines I want to show.
\lstnewenvironment{codepart}[3][style=C++]
{\lstset{firstline={#2}, lastline={#3}, #1}}%
{}
I was able to use this environment successfully like that.
% Show lines from 2 - 16
\begin{frame}[t, fragile]{Code Block 1}
\begin{codepart}[style=C++]{2}{16}%
class device_t
{
public:
void trigger(ostream& out) const
{
out << "Device triggered\n";
}
};
using sensor_t = vector<device_t>;
void trigger(const sensor_t& s, ostream& out)
{
out << "Sensor triggering...\n";
for (const auto& d : s)
d.trigger(out);
out << "Sensor triggered!\n";
}
\end{codepart}
\end{frame}
My solution to the second sub-problem was to create a new command that uses counters and the multido package to calculate the line range of each block of code.
\usepackage{multido}
% 1 -> step (optional), 2 -> startline, 3 -> endline, 4 -> maxLines, 5 -> codeblock
\newcommand{\ForEachBlock}[5][1]
{%
% Calculate the number of lines to be shown as endline - startline + 1 (e.g 19 - 2 + 1 = 18 lines)
\newcounter{numOfLines}
\setcounter{numOfLines}{#3}
\addtocounter{numOfLines}{-#2}
\addtocounter{numOfLines}{1}
% Calculate the number of iterations needed based on the maxLines that can be shown in each slide (eg. 18 - 15 + 1 = 4 slides (2->16), (3->17), (4->18), (5->19)
\newcounter{iterations}
\setcounter{iterations}{\value{numOfLines}}
\addtocounter{iterations}{-#4}
\addtocounter{iterations}{1}
% Calculate the end line of the first block.
\newcounter{firstEndLine}
\setcounter{firstEndLine}{#2}
\addtocounter{firstEndLine}{#4}
\addtocounter{firstEndLine}{-1}
% For each block calculate the start and end lines
\multido{\i=1+1, \isl=#2+#1, \iel=\value{firstEndLine}+#1}{\value{iterations}}
{%
#5 % <- All my code goes here
}
}
I verified the results like that.
\begin{frame}[t, fragile]{Code Block ranges}
\ForEachBlock{2}{19}{15}
{%
Code block No \i starts at: \isl and ends at: \iel \endgraf
}
\end{frame}
Now I want to use the 'codepart' environment and the ForEachBlock command to create the 'codescroll' environment I described earlier. What I have in mind, is inside the ForEachBlock command to call the codepart environment with different arguments each time.
Unfortunately, the following code is not functional.
\newenvironment<>{codescroll}[4][1]
{
% Create n (4) slides by using \ForEachBlock and codepart environment e.g.
\ForEachBlock{#2}{#3}{#4}{
\begin{codepart}<\i>{\isl}{\iel}
}
}
{\end{codepart}}
Am I missing something?
The desired usage of the codescroll environment and the result I want to produce are shown as comments in the compilable code snippet bellow.
\documentclass{beamer}
% Code colors (Irrelevant from the presentation color theme!)
\definecolor{codemaincolor}{RGB}{0, 0, 0}
\definecolor{codebackgroundcolor}{RGB}{255, 255, 255}
\definecolor{codekeywordcolor}{RGB}{0, 0, 255}
\definecolor{codestringcolor}{RGB}{163, 21, 21}
\definecolor{codecommentcolor}{RGB}{39, 139, 39}
\definecolor{codeusertypecolor}{RGB}{43, 145, 175}
\usepackage{listings}
\usepackage{lstautogobble}
\lstset {
basicstyle={\scriptsize \ttfamily \color{codemaincolor}},
backgroundcolor=\color{codebackgroundcolor},
autogobble = true,
tabsize = 2,
xleftmargin=0pt,
xrightmargin=0pt,
aboveskip=0pt, % \medskipamount,
belowskip=0pt, % \medskipamount,
literate={\ \ }{{\ }}1
}
% Code C++ style
\lstdefinestyle{C++} {
language=C++,
otherkeywords = {final, override, noexcept},
keywordstyle = {\color{codekeywordcolor}},
stringstyle = {\color{codestringcolor}},
commentstyle = {\color{codecommentcolor}\em},
% Class and types highlighting
classoffset=1, % starting new class
morekeywords={vector, ostream, unique_ptr, shared_ptr, T, device_t, abstract_device, device_one, device_two, device_three, executable_device, measurable_device, my_device, concept_t, model, device_one_model, device_two_model, sensor_t, history_t},
keywordstyle=\color{codeusertypecolor},
classoffset=0,
}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{multido}
\usetheme{Madrid}
\lstnewenvironment{codepart}[3][style=C++]
{\lstset{firstline={#2}, lastline={#3}, #1}}%
{}
% For command
% 1 -> step (optional), 2 -> startline, 3 -> endline, 4 -> maxLines, 5 -> codeblock
\newcommand{\ForEachBlock}[5][1]
{%
% Calculate the number of lines to be shown as endline - startline + 1 (e.g 19 - 2 + 1 = 18 lines)
\newcounter{numOfLines}
\setcounter{numOfLines}{#3}
\addtocounter{numOfLines}{-#2}
\addtocounter{numOfLines}{1}
% Calculate the number of iterations needed based on the maxLines that can be shown in each slide (eg. 18 - 15 + 1 = 4 slides (2->16), (3->17), (4->18), (5->19)
\newcounter{iterations}
\setcounter{iterations}{\value{numOfLines}}
\addtocounter{iterations}{-#4}
\addtocounter{iterations}{1}
% Calculate the end line of the first block.
\newcounter{firstEndLine}
\setcounter{firstEndLine}{#2}
\addtocounter{firstEndLine}{#4}
\addtocounter{firstEndLine}{-1}
% For each block calculate the start and end lines
\multido{\i=1+1, \isl=#2+#1, \iel=\value{firstEndLine}+#1}{\value{iterations}}
{%
#5 % <- All my code goes here
}
}
% % Desired environment
% % 1 -> step (optional), 2 -> startline, 3 -> endline, 4 -> maxLines
% \newenvironment<>{codescroll}[4][1]
% {
% % Create n (4) slides by using \ForEachBlock and codepart environment e.g.
% \ForEachBlock{#2}{#3}{#4}{
% \begin{codepart}<\i>{\isl}{\iel}
% }
% }
% {\end{codepart}}
\begin{document}
\begin{frame}[t, fragile]{Code Block ranges}
\ForEachBlock{2}{19}{15}
{%
Code block No \i starts at: \isl and ends at: \iel \endgraf
}
\end{frame}
\begin{frame}[t, fragile]{Code Block 1}
\begin{codepart}[style=C++]{2}{16}%
class device_t
{
public:
void trigger(ostream& out) const
{
out << "Device triggered\n";
}
};
using sensor_t = vector<device_t>;
void trigger(const sensor_t& s, ostream& out)
{
out << "Sensor triggering...\n";
for (const auto& d : s)
d.trigger(out);
out << "Sensor triggered!\n";
}
\end{codepart}
\end{frame}
\begin{frame}[t, fragile]{Code Block 2}
\begin{codepart}[style=C++]{3}{17}%
class device_t
{
public:
void trigger(ostream& out) const
{
out << "Device triggered\n";
}
};
using sensor_t = vector<device_t>;
void trigger(const sensor_t& s, ostream& out)
{
out << "Sensor triggering...\n";
for (const auto& d : s)
d.trigger(out);
out << "Sensor triggered!\n";
}
\end{codepart}
\end{frame}
\begin{frame}[t, fragile]{Code Block 3}
\begin{codepart}[style=C++]{4}{18}%
class device_t
{
public:
void trigger(ostream& out) const
{
out << "Device triggered\n";
}
};
using sensor_t = vector<device_t>;
void trigger(const sensor_t& s, ostream& out)
{
out << "Sensor triggering...\n";
for (const auto& d : s)
d.trigger(out);
out << "Sensor triggered!\n";
}
\end{codepart}
\end{frame}
\begin{frame}[t, fragile]{Code Block 4}
\begin{codepart}[style=C++]{5}{19}%
class device_t
{
public:
void trigger(ostream& out) const
{
out << "Device triggered\n";
}
};
using sensor_t = vector<device_t>;
void trigger(const sensor_t& s, ostream& out)
{
out << "Sensor triggering...\n";
for (const auto& d : s)
d.trigger(out);
out << "Sensor triggered!\n";
}
\end{codepart}
\end{frame}
% \begin{frame}[t, fragile]{Scroll}
% \ForEachBlock{2}{9}{5}
% {%
%
% class device_t
% {
% public:
% void trigger(ostream& out) const
% {
% out << "Device triggered\n";
% }
% };
%
% using sensor_t = vector<device_t>;
%
% void trigger(const sensor_t& s, ostream& out)
% {
% out << "Sensor triggering...\n";
% for (const auto& d : s)
% d.trigger(out);
% out << "Sensor triggered!\n";
% }
% \end{frame}
% Desired call of the environment
% I want to scroll from line 2 to line 19 with (optional) step 1 when the page size is 15 lines
% \begin{frame}[t, fragile]{Scroll}
% \begin{codescroll}[1]{2}{19}{15}%
%
% class device_t
% {
% public:
% void trigger(ostream& out) const
% {
% out << "Device triggered\n";
% }
% };
%
% using sensor_t = vector<device_t>;
%
% void trigger(const sensor_t& s, ostream& out)
% {
% out << "Sensor triggering...\n";
% for (const auto& d : s)
% d.trigger(out);
% out << "Sensor triggered!\n";
% }
% \end{codescroll}
% \end{frame}
% Desired output of the frame
% Slide 1
% class device_t
% {
% public:
% void trigger(ostream& out) const
% {
% out << "Device triggered\n";
% }
% };
% using sensor_t = vector<device_t>;
% void trigger(const sensor_t& s, ostream& out)
% {
% out << "Sensor triggering...\n";
% for (const auto& d : s)
%---------------
% Slide 2
% {
% public:
% void trigger(ostream& out) const
% {
% out << "Device triggered\n";
% }
% };
% using sensor_t = vector<device_t>;
% void trigger(const sensor_t& s, ostream& out)
% {
% out << "Sensor triggering...\n";
% for (const auto& d : s)
% d.trigger(out);
%---------------
% Slide 3
% public:
% void trigger(ostream& out) const
% {
% out << "Device triggered\n";
% }
% };
% using sensor_t = vector<device_t>;
% void trigger(const sensor_t& s, ostream& out)
% {
% out << "Sensor triggering...\n";
% for (const auto& d : s)
% d.trigger(out);
% out << "Sensor triggered!\n";
%---------------
% Slide 4
% void trigger(ostream& out) const
% {
% out << "Device triggered\n";
% }
% };
% using sensor_t = vector<device_t>;
% void trigger(const sensor_t& s, ostream& out)
% {
% out << "Sensor triggering...\n";
% for (const auto& d : s)
% d.trigger(out);
% out << "Sensor triggered!\n";
% }
%---------------
\end{document}
Thank you in advance for your help,


listings-based solution. Also, It seems that questions marked "duplicate" cannot be answered. – AlexG Aug 26 '19 at 18:13