自动连接相邻的 tcolorboxes

自动连接相邻的 tcolorboxes

我希望一些相邻的tcolorboxes 能够连接起来,但不使用选项beforeafter skip=0pt,因为我还希望这些框与其他文本有正常的间距。也就是说,我希望这些框如下图所示:

在此处输入图片描述

自动更改为下图所示:

在此处输入图片描述

如何才能实现这一目标?

下面是一个可供玩的 MWE。

\documentclass{article}

\usepackage[many]{tcolorbox}
\usepackage{blindtext}

\newtcolorbox{definition}{enhanced jigsaw,pad at break*=1mm,breakable,
left=4mm,right=4mm,top=1mm,bottom=1mm,
% beforeafter skip balanced=0pt,
colback=orange!10,boxrule=0pt,frame hidden,
borderline west={1.5mm}{-1mm}{green!50!black},arc=.7mm}

\begin{document}

\begin{definition}
    ...
\end{definition}

\begin{definition}
    \blindtext
\end{definition}

\end{document}

老的

我写了以下(非常丑陋的)代码:

\documentclass{article}
\usepackage[many]{tcolorbox}

% \newenvironment{testbox}
% {\begin{tcolorbox}
% [enhanced jigsaw,pad at break*=1mm,breakable,
% colback=orange!10!white,boxrule=0pt,frame hidden,
% borderline west={1.5mm}{-1mm}{green}]}
% {\end{tcolorbox}}

\usepackage[user,savepos]{zref}
\usepackage{xifthen}

\newlength{\deftop}
\newlength{\defbot}
\newlength{\defsep}
\newcounter{def}

\newenvironment{testbox}
{\vspace{-1mm}\begin{tcolorbox}[blank,breakable]
\stepcounter{def}
\zsavepos{def\arabic{def}1}
\setlength{\global\deftop}{
  \dimexpr\paperheight - \zposy{def\arabic{def}1} sp}
\setlength{\global\defsep}{\deftop-\defbot}
\ifthenelse{\lengthtest{\defsep > -0.1mm}}
  {\ifthenelse{\lengthtest{\defsep < 3mm}}{\vspace{-4mm}}{}}
  {}\par
  % This should be a dynamical quantity,
  % adjusting according to the page's situation
\begin{tcolorbox}
[enhanced jigsaw,pad at break*=1mm,breakable,
colback=orange!10!white,boxrule=0pt,frame hidden,
borderline west={1.5mm}{-1mm}{green}]}
{\end{tcolorbox}
\zsavepos{def\arabic{def}2}
\setlength{\global\defbot}{
  \dimexpr\paperheight - \zposy{def\arabic{def}2} sp}
\end{tcolorbox}\vspace{-1mm}}

\begin{document}
    Some texts.
    \begin{testbox}box 1\end{testbox}
    Some texts.
    \begin{testbox}box 2\end{testbox}%\vspace{-4mm}
    \begin{testbox}box 3\end{testbox}
\end{document}

它用zref记录每个框的位置,以便检查两个框是否相邻。然而,这个解决方案不是总是有效。此代码不检查两个框是否在同一页上(我确实编写了一个尝试检查页码问题的版本,但由于 LaTeX 中的页码检查在页面边缘效果不佳,因此我放弃了它)。此外,这4mm不是一个好主意,因为有时当页面非常松散时,\vspace{4mm}不足以将它们连接起来。

我想知道您是否有更好更漂亮的方法来实现这种效果。

答案1

以下是使用我最喜欢的expl3工具库中的一种方法:\peek_analysis_map_inline:n。它允许您逐个标记扫描输入流,让您根据需要对每个标记进行操作。命令\ScanEnv

\ScanEnv [*] {<env>} {<true>} {<false>}

在标记流中向前查找,忽略空格(如果*给出,则忽略\par),寻找\begin{<env>}。如果找到,它会<true>在忽略的空格和之前插入代码\par。如果找不到环境,<false>则插入代码。

有了它,你可以做你想做的事

\ScanEnv* {definition} {\vspace{-\baselineskip}} {}

加上一个,\AfterEnvEnd以便能够使用结束环境钩子插入代码。如果要扫描多个环境,则需要嵌套调用:

\ScanEnv* {definition} {\vspace{-\baselineskip}}
  {\ScanEnv* {definition*} {\vspace{-\baselineskip}} {}}

在此处输入图片描述

代码如下:

\RequirePackage{xparse}
\ExplSyntaxOn
\makeatletter
\NewDocumentCommand \AfterEnvEnd { +m }
  { \jinwen_after_env_end:nw {#1} }
\cs_new_protected:Npn \jinwen_after_env_end:nw #1 #2
       \if@ignore\@ignorefalse\ignorespaces\fi
  { #2 \if@ignore\@ignorefalse\ignorespaces\fi #1 }
\makeatother
\NewDocumentCommand \ScanEnv { s m +m+m }
  {
    \IfBooleanTF {#1}
      { \jinwen_scan_env_ignore_par:nTF }
      { \jinwen_scan_env:nTF }
          {#2} {#3} {#4}
  }
\cs_new_protected:Npn \jinwen_scan_env:nTF
  { \__jinwen_scan_env:NnTF \c_false_bool }
\cs_new_protected:Npn \jinwen_scan_env_ignore_par:nTF
  { \__jinwen_scan_env:NnTF \c_true_bool }
\tl_new:N \l__jinwen_collected_tl
\cs_new_protected:Npn \__jinwen_scan_env:NnTF #1 #2 #3 #4
  {
    \tl_clear:N \l__jinwen_collected_tl
    \peek_analysis_map_inline:n
      {
        \tl_put_right:Nn \l__jinwen_collected_tl {##1}
        \int_compare:nNnTF { "##3 } = { 0 }
          {
            \exp_args:No \token_if_eq_meaning:NNTF {##1} \begin
              { \peek_analysis_map_break:n { \__jinwen_chk_env:nTFn {#2} {#3} {#4} } }
              {
                \bool_lazy_and:nnF {#1}
                    { \exp_args:No \token_if_eq_meaning_p:NN {##1} \par }
                  { \__jinwen_scan_env_end:n {#4} }
              }
          }
          { \int_compare:nNnF { "##3 } = { 10 } { \__jinwen_scan_env_end:n {#4} } }
      }
  }
\cs_new_protected:Npn \__jinwen_scan_env_end:n #1
  { \peek_analysis_map_break:n { \__jinwen_reinsert_tokens:nn {#1} { } } }
\cs_new_protected:Npn \__jinwen_reinsert_tokens:nn #1 #2
  {
    \use:x
      {
        \tl_clear:N \exp_not:N \l__jinwen_collected_tl
        \exp_not:n {#1} \l__jinwen_collected_tl #2
      }
  }
\cs_new_protected:Npn \__jinwen_chk_env:nTFn #1 #2 #3 #4
  {
    \exp_args:Nx \__jinwen_reinsert_tokens:nn
      { \str_if_eq:nnTF {#1} {#4} { \exp_not:n {#2} } { \exp_not:n {#3} } } { {#4} }
  }
\ExplSyntaxOff

\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{blindtext}

\newtcolorbox{definition}{enhanced jigsaw,pad at break*=1mm,breakable,
left=4mm,right=4mm,top=1mm,bottom=1mm,
% beforeafter skip balanced=0pt,
colback=orange!10,boxrule=0pt,frame hidden,
borderline west={1.5mm}{-1mm}{green!50!black},arc=.7mm}

\newtcolorbox{definition*}{enhanced jigsaw,pad at break*=1mm,breakable,
left=4mm,right=4mm,top=1mm,bottom=1mm,
% beforeafter skip balanced=0pt,
colback=red!10,boxrule=0pt,frame hidden,
borderline west={1.5mm}{-1mm}{green!50!black},arc=.7mm}

\def\scandefinitionenv{%
  \AfterEnvEnd{%
    \ScanEnv*{definition}%
      {\vspace{-\baselineskip}}%
      {\ScanEnv*{definition*}%
        {\vspace{-\baselineskip}}%
        {}}}}

\AddToHook{env/definition/end}{\scandefinitionenv}%
\AddToHook{env/definition*/end}{\scandefinitionenv}%

\begin{document}

\begin{definition}
  This is followed by text
\end{definition}

something else

\begin{definition}
  This is followed by another definition
\end{definition}

\begin{definition*}
  See, no gap :)
\end{definition*}

\begin{quote}
  some other environment
\end{quote}

\end{document}

相关内容