我希望一些相邻的tcolorbox
es 能够连接起来,但不使用选项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}