对话颜色编码 - 不同颜色框中的替代段落

对话颜色编码 - 不同颜色框中的替代段落
  • 我想定义一个命令\dialoguestart{black!10}{black!25},开始将段落设置为tcolorboxes,在较浅(黑色!10)和较深(黑色!25)之间交替。
  • 在另一个命令之后应返回段落的默认设置\dialogueend

编辑:在与@Schrödinger's cat、@siracusa 和 @cfr 讨论后,很明显最初的问题太大而且太不具体。我发现解决这个问题的一个合理方法是使用环境,我已经相应地调整了帖子。

为了说明这一点,以下代码片段

\documentclass{article}

\usepackage{lipsum}
\usepackage[breakable]{tcolorbox}
%\input{solution_to_this_problem}

\begin{document}

\lipsum[66]
\dialoguestart{black!10}{black!25}
\lipsum[75]
\lipsum[66]
\dialogueend
\lipsum[75]

\end{document}

应该产生

在此处输入图片描述

下面的 MWE 产生了所需的结果。但是,它并没有完全满足我的需求,因为新环境一次只能处理一个段落。

\documentclass{article}
\usepackage{lipsum}
\usepackage[breakable]{tcolorbox}

\newif\iflight\lightfalse
\newcommand\toggleboxstart{%
    \iflight\begin{dark}%
    \else\begin{light}\fi}
\newcommand\toggleboxend{%
  \iflight\end{dark}\global\lightfalse%
  \else\end{light}\global\lighttrue\fi}
\newenvironment{toggling}[2]{%
    \newtcolorbox{light}{colback=#1,colframe=#1,sharp corners,breakable} 
    \newtcolorbox{dark}{colback=#2,colframe=#2,sharp corners,breakable}
    % something that ensures `\toggleboxend\toggleboxstart` being called between paragraphs
    }

\newcommand\dialoguestart[2]{\begin{toggling}{#1}{#2}\toggleboxstart}
\newcommand\dialogueend{\toggleboxend\end{toggling}}

\begin{document}
\lipsum[66]
\dialoguestart{black!10}{black!25}
\lipsum[75]
\dialogueend                       % these two commands
\dialoguestart{black!10}{black!25} % I would like to avoid
\lipsum[66]
\dialogueend
\lipsum[75]
\end{document}

处理多个段落,\toggleboxend必须\toggleboxstart在切换环境中的段落之间调用。我尝试添加

\let\origpar\par
\let\origeverypar\everypar
\renewcommand\everypar{\origeverypar\toggleboxstart}
\renewcommand\par{\toggleboxend\origpar}

到环境的定义,这会抛出Paragraph ended before \renew@command was complete。并使用 {everyhook} 包推送我的命令,这解决了这个问题类似于手头上的

\usepackage{everyhook}
\newenvironment{toggling}[2]{%
    \newtcolorbox{light}{colback=#1,colframe=#1,sharp corners,breakable} 
    \newtcolorbox{dark}{colback=#2,colframe=#2,sharp corners,breakable}
    \PushPreHook{par}{\toggleboxstart}
    \PushPostHook{par}{\toggleboxend}
    }

\newcommand\dialoguestart[2]{\begin{toggling}{#1}{#2}} % no more \toggleboxstart
\newcommand\dialogueend{\end{toggling}}                % no more \toggleboxend

结果提示TeX capacity exceeded, sorry [grouping levels=255]。我还能做什么来执行段落之间的一些代码?在环境中,只会有纯文本。


编辑(经过贾昆谁发起了第二次赏金):我尝试将我的代码复制并粘贴到赏金描述中但没有成功,因此我再次将其发布在这里以提高可读性:

\documentclass{article}
\usepackage{lipsum}
\usepackage[breakable]{tcolorbox}

\newif\iflight

\newenvironment{toggling}[2]{%
    \newcommand\toggleboxstart{%
        \iflight
            %(BEGIN LIGHT)
            \begin{tcolorbox}[colback=#1,colframe=#1,sharp corners,breakable]%
        \else
            %(BEGIN DARK)
            \begin{tcolorbox}[colback=#2,colframe=#2,sharp corners,breakable]%
        \fi
    }%
    \newcommand\toggleboxend{%
        \end{tcolorbox}\relax
        %(END)
        \iflight
            \global\lightfalse
        \else
            \global\lighttrue
        \fi
    }%
    \toggleboxstart
    \everypar{\toggleboxend\toggleboxstart}%
}{%
    \toggleboxend
}

\begin{document}
    \lipsum[66]
    \begin{toggling}{black!10}{black!25}
    \lipsum[75]

    \lipsum[66]

    \lipsum[66]
    \end{toggling}
    \lipsum[75]
\end{document}

此代码失败

LaTeX 错误:\begin{tcolorbox} 在输入第 36 行以 \end{tcb@savebox} 结束。

或者

额外},或者被遗忘的\endgroup。

当注释掉的可选参数时\begin{tcolorbox}

为什么这不起作用以及如何使其起作用?

(当注释掉\begin{tcolorbox}\end{tcolorbox}并在括号中的测试文本中注释时,我得到了预期的输出。)

编辑:我正在使用以下版本(grep -i version main.log):

This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019/Arch Linux) (preloaded format=pdflatex 2019.11.15)  25 NOV 2019 21:59
Package: tcolorbox 2019/09/19 version 4.21 text color boxes
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2019/08/03 v3.1.4b (3.1.4b)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2019/08/03 v3.1.4b (3.1.4b)
Library (tcolorbox): 'tcbbreakable.code.tex' version '4.21'
[Loading MPS to PDF converter (version 2006.09.02).]

我尝试使用 tcolorbox 2019/11/15 版本 4.22 并得到相同的结果。

答案1

我不知道为什么,但我无法轻松地用 运行它\everypar。(在段落之间插入虚拟文本效果很好,但用 替换该虚拟文本\end{tcolorbox}\begin{tcolorbox}会导致Extra }, or forgotten \endgroup.错误。)

相反,我提供了一种不同的方法,即用分隔参数替换段落结尾。它的缺点是效率较低,并且会预先读取整个“环境”主体。因此,不要尝试使用任何依赖于更改 catcode 的东西,例如\verb其中的命令。但实现起来相当简单:

\documentclass{article}

\usepackage{lipsum}
\usepackage[breakable]{tcolorbox}

% soluton_to_this_problem START
\long\def\dialoguestart#1#2#3\dialogueend{% #1: color 1, #2: color 2, #3: body
    \dialogueiter{#1}{#2}#3\par\dialogueend
}

\long\def\dialogueiter#1#2#3\par#4\dialogueend{% #1: this color, #2: alternative color, #3: next paragraph, #4: rest of body
    \begin{tcolorbox}[colback=#1,colframe=#1,sharp corners,breakable]
        #3
    \end{tcolorbox}
    \if\relax\detokenize{#4}\relax
        % #4 is empty => finish
    \else
        % note how I have swapped #1 and #2 to toggle the colors
        \dialogueiter{#2}{#1}#4\dialogueend
    \fi
}
% soluton_to_this_problem END

\begin{document}

\lipsum[66]
\dialoguestart{black!10}{black!25}
\lipsum[75]

\lipsum[66]

\lipsum[66]
\dialogueend
\lipsum[75]

\end{document}

\dialoguestart只是\dialogueiter宏的一个包装器,以避免在参数不包含段落结尾(段落结尾 = 空行 = \par)时出现错误。

\dialogueiter是执行实际工作的宏。它使用参数分隔符将参数分成两部分。第一部分是下一段,第二部分是剩余部分。如果剩余部分不为空,\dialogueiter则使用递归进一步处理它。

\dialogueend只是一个参数分隔符,永远不会扩展或执行。

在此处输入图片描述


更新:xparse 包提供了(除其他外)\NewDocumentCommand比 LaTeX2e 宏更强大的宏,\newcommand允许使用分隔参数(这就是我没有\newcommand在本答案中使用的原因)和几个可选参数。此外,在这种情况下,它比 TeX 原语(我上面使用过)更舒适,\def因为它支持可选参数。

\NewDocumentCommand也比它更安全,\def因为它会检查该名称的宏是否已经存在,如果存在则打印错误,而不是默默地覆盖现有的宏。

(如果您想实现可选参数,\def您可以提前查找下一个标记\kernel@ifnextchar,然后使用需要该参数的宏或不需要该参数的另一个宏。)

\usepackage{xparse}

\NewDocumentCommand{\dialoguestart}{O{black!10} O{black!25} +u\dialogueend}{% [#1: color 1], [#2: color 2], #3: body
    \dialogueiter[#1][#2]#3\par\dialogueend
}

\NewDocumentCommand{\dialogueiter}{r[] r[] u\par +u\dialogueend}{% #1: this color, #2: alternative color, #3: next paragraph, #4: rest of body
    \begin{tcolorbox}[colback=#1,colframe=#1,sharp corners,breakable]
        #3
    \end{tcolorbox}
    \if\relax\detokenize{#4}\relax
        % #4 is empty => finish
    \else
        % note how I have swapped #1 and #2 to toggle the colors
        \dialogueiter[#2][#1]#4\dialogueend
    \fi
}

相关内容