2

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

  1. The first (start) line of the part of the code I want to present.
  2. The last (end) line of the part of the code I want to present.
  3. 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}

enter image description here

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}

enter image description here

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,

Jason
  • 51
  • Welcome to TeX.SE! Can you please make your last given code snippet compilable? Then we do not have to guess, what you are doing ... For example I did not saw in the code snippets above a documentclass and the used options for it ... – Mensch Aug 04 '19 at 20:46
  • I made one minimal example here I also added comments that explain what I want to achieve. Thank you for your time spend looking into that. – Jason Aug 06 '19 at 20:38
  • 2
    Sorry, I do not have an overleaf account and I do not want to create one. Please -- as usual here -- add your short compilable tex code into your question. Then the code is still there even if the given link is broken ... – Mensch Aug 06 '19 at 20:42
  • I updated the last example with the multido package. I also wrote as comments the desired environment and the output I want to produce. – Jason Aug 06 '19 at 21:02
  • I managed to make the multido package to work correctly, but calling an environment inside that command is not possible. Any ideas on that? – Jason Aug 12 '19 at 19:49
  • Related: https://tex.stackexchange.com/q/309309 , https://tex.stackexchange.com/q/283564 , https://tex.stackexchange.com/q/37869 . – AlexG Aug 13 '19 at 08:08
  • Thank you AlexG! The first related issue seems to have what I want. I will try to modify this example and use the listings package instead of minted. Have you ever tried to use this scrolling environment with a remote controller during the presentation? I might have to remove the auto scrolling on click. Thank you again for your time. – Jason Aug 13 '19 at 18:00
  • @Jason : The presenter tool needs to be able to move the mouse pointer and to produce left mouse button clicks. Unfortunately, there is no means to read keypresses from the keyboard by JavaScript. – AlexG Aug 14 '19 at 06:53
  • My problem as AlexG metioned in the comments is very similar to this question here so I modified the solution to fit my needs. In particular, I created the codescroll environment by using the \lstnewenvironment command and the scrolling functions provided by the AlexG solution. – Jason Aug 26 '19 at 16:21
  • Hey @AlexG shall I post my solution here in case someone else is interested? Should I modify the initial post to mention that I want lstlistings environment? I created a minimal working solution here – Jason Aug 26 '19 at 16:27
  • @Jason . Looks good! I'd suggest you post your answer to the other question, which is general enough to also allow a listings-based solution. Also, It seems that questions marked "duplicate" cannot be answered. – AlexG Aug 26 '19 at 18:13

0 Answers0