3

I asked this question, which was solved but didn't help me with the issue I was trying to fix. Seems I fell for the XY problem, so I'll make it more explicit.

What I want: a package that has a variable called solutions and an environment \begin{solution}\end{solution} which when solutions is set will shade the background of the text that is inside it and when not set will remove everything (basically as if I hadn't written anything at all).

What I've tried so far:

\ProvidesPackage{solutions}

\newif\ifsolutions
\solutionsfalse
% If solution is false the environment should blank whatever is inside it
% If true it should shade the background gray
\DeclareOption{solutions}{\solutionstrue}
\ProcessOptions\relax
\newenvironment{solution}{
    \ifsolutions
       #1 $\delta$ 
    \else 
    \fi
}{
    \ifsolutions
       #1 $\delta$  % playing with \delta just to see something
    \else   
    \fi
}

Just using a \delta because I thought it would have been easier to see. But this doesn't work at all since I don't think there is a way to remove whats inside the environment from within it. I need something modifying it from outside.

5
  • Does this answer solve your issue?
    – jlab
    Commented Aug 15 at 14:22
  • Ignoring the conditional, it looks like you're not quite writing the environment definition correctly to begin with. What is #1? Are you having trouble with the shading part, or with the ignoring part, or both? For ignoring, I would just \usepackage{comment} and then \excludecomment{solution}.
    – Teepeemm
    Commented Aug 15 at 14:39
  • @Teepeemm you're right the #1 was a mistake I was making thinking I needed to use arguments. Forgot to remove it.
    – aram
    Commented Aug 15 at 16:09
  • @jlab I don't think so, I read that and I kind of understood what was wrong with mine but not how to implement my current predicament.
    – aram
    Commented Aug 15 at 16:10
  • Probably I might have the package option toggle the solution environment between placing things into the document and placing things into a box, like the lrbox-environment does, but which is never typeset. This way, at least counters keep being incremented. Things might turn out less trivial when it comes to delayed-writing things coming from processing the environment's body to external text file even when the environment's content is not to be typeset... Commented Aug 15 at 22:35

3 Answers 3

4

Based on \NewDocumentEnvironment, \colorbox this is rather straight forward. Please keep in mind that \colorbox doesn't allow page breaks inside your solutions.

\documentclass[]{article}

\begin{filecontents}[overwrite]{solutions.sty}
\RequirePackage{xcolor}

\newif\ifsolutions
\solutionsfalse
% If solution is false the environment should blank whatever is inside it
% If true it should shade the background gray
\DeclareOption{solutions}{\solutionstrue}
\ProcessOptions\relax

\NewDocumentEnvironment{solution}{m +b}
  {%
    \ifsolutions
      \expandafter\@secondoftwo
    \fi
    \@gobble
      {%
        #1 $\delta$
        \par
        \noindent
        \colorbox[gray]{0.9}
          {%
            \begin{minipage}{\dimexpr\linewidth-2\fboxsep\relax}%
              #2%
            \end{minipage}%
          }%
        \par
        #1 $\delta$
      }%
  }
  {}
\end{filecontents}

\usepackage[solutions]{solutions}

\begin{document}
You had a question
\begin{solution}{a}
  This is a solution to your problem.
\end{solution}
\end{document}

enter image description here

On how this works:

The +b argument of a \NewDocumentEnvironment will collect the contents of that environment as an argument, in the definition you can then refer to said contents by #2 (in this case, as it's the second argument).

The \ifsolutions is evaluated, but the contents aren't immediately put inside that \if... conditional, as that can get unstable in TeX. Instead we put an argument after the \if...\fi part, and if the conditional is true execute that argument (\expandafter\@secondoftwo first expands the \fi closing the conditional block, then executes \@secondoftwo that's defined as \providecommand\@secondoftwo[2]{#2}). If the conditional is false everything up to \fi will be removed and \@gobble will gobble up the contents (\@gobble is defined as \providecommand\@gobble[1]{}).


A variant that displays another symbol if \ifsolutions is false:

\documentclass[]{article}

\begin{filecontents}[overwrite]{solutions.sty}
\RequirePackage{xcolor}

\newif\ifsolutions
\solutionsfalse
% If solution is false the environment should blank whatever is inside it
% If true it should shade the background gray
\DeclareOption{solutions}{\solutionstrue}
\ProcessOptions\relax

\providecommand\@secondofthree[3]{#2}

\NewDocumentEnvironment{solution}{m +b}
  {%
    \ifsolutions
      \expandafter\@secondofthree
    \fi
    \@secondoftwo
      {%
        #1 $\delta$
        \par
        \noindent
        \colorbox[gray]{0.9}
          {%
            \begin{minipage}{\dimexpr\linewidth-2\fboxsep\relax}%
              #2%
            \end{minipage}%
          }%
        \par
        #1 $\delta$
      }%
      {$\alpha$}% this shows up if \ifsolutions is false
  }
  {}
\end{filecontents}

\usepackage[solutions]{solutions}

\begin{document}
You had a question
\begin{solution}{a}
  This is a solution to your problem.
\end{solution}
\end{document}
4
  • Could you expand a bit how does this works? Also I dont want the \delta I just wanted to show that symbol when it was true and another when it was false (think of it like the greybox but much simpler before I didn't have the if else etc).
    – aram
    Commented Aug 15 at 21:47
  • @aram if you don't want the \delta just remove it. If you want to show something else if \ifsolutions is false just do so, I update my code to show a simple way of doing so.
    – Skillmon
    Commented Aug 16 at 11:59
  • I ended up using your solution removing the \delta see my answer tex.stackexchange.com/a/724515/35209
    – aram
    Commented Aug 16 at 19:17
  • Oh also ended up using framed instead of minipage.
    – aram
    Commented Aug 16 at 19:17
2

This is similar to Skillman's answer in that it uses \NewDocumentEnvironment and \colorbox. Since questions are usually typeset as lists, however, this tries to adapt to an enumerate or itemize environment, if applicable. To do this, the width of the minipage inside the box is adjusted to account for the indentation of the list and paragraph indentation is restored for paragraphs after the first1.

shaded solutions aligned with list indentation

\begin{filecontents}[overwrite]{solutions.sty}
\ProvidesPackage{solutions}

\newif\ifsolutions
\solutionsfalse
% If solution is false the environment should blank whatever is inside it
% If true it should shade the background gray
\DeclareOption{solutions}{\solutionstrue}
\ProcessOptions\relax
\RequirePackage{calc}
\RequirePackage{xcolor}
\newlength\normalparindent
\AddToHook{begindocument}{\setlength\normalparindent{\parindent}}
\NewDocumentEnvironment {solution} { +b }
{%
  \ifsolutions
    \par
    \colorbox{gray!15}{%
      \begin{minipage}{\linewidth-\@totalleftmargin-2\fboxsep}
        \AddToHookNext{para/after}{%
          \setlength\parindent{\normalparindent}%
        }%
        #1
      \end{minipage}%
    }%
  \fi
}{}
\end{filecontents}
\documentclass{article}
\usepackage[solutions]{solutions}
% \usepackage{solutions}
\begin{document}
\begin{enumerate}
  \item a question
    \begin{solution}
      a solution
    \end{solution}
  \item another question
    \begin{solution}
      a somewhat longer solution which goes into much more detail and requires line-breaking, paragraphs and similar bells, whistles and such-like.

      here's a new paragraph just for the sake of making a paragraph, really, since there's no topical content to this as it is all properly classified as `waffle'.

      \begin{center}
        \begin{tabular}{ll}
          \hline
          one & two \\
          \hline
        \end{tabular}
      \end{center}

      \begin{equation}
        v = ut + \frac{1}{2}at^{2}
      \end{equation}
    \end{solution}
\end{enumerate}
\end{document}

If you need to allow page breaks within solutions, I would look at the tcolorbox package which has a library for enabling breakable boxes.

1Whether this is preferred to boxes which span the text width depends on personal preference and use case, so adjusting the alignment isn't necessarily better.

0

I ended up using both answers to put something together that fits what I want to

\ProvidesPackage{solutions}

\newif\ifsolutions
\solutionsfalse
% If solution is false the environment should blank whatever is inside it
% If true it should shade the background gray
\DeclareOption{solutions}{\solutionstrue}
\DeclareOption{nosolutions}{\solutionsfalse}
\ProcessOptions\relax
\RequirePackage{xcolor}
\NewDocumentEnvironment{solution} { +b }
{%
  \ifsolutions
    \par
    \noindent
    \colorbox{gray!15}
      {%
        \begin{minipage}{\dimexpr\linewidth-2\fboxsep\relax}%
          #1%
        \end{minipage}%
      }%%
  \fi
}{}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .