下面的代码将获取一段文本,并使用 tcolorbox 将每段以空白行结尾的文本放入一个框中。每个输入前面都有时间戳。我希望代码用它找到的第一段以空白行结尾的文本填充框的上半部分,字体较小,然后用第二行填充下半部分,依此类推。如果在最后一个框中只找到一段文本,那么它应该填充一个框而不使用 \tcblower。
我开始编写一个新命令,即“bubbledual”,但环境“rightbubbles”必须进一步开发。
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{varwidth}
\usepackage{environ}
\usepackage{xparse}
\newlength{\bubblesep}
\newlength{\bubblewidth}
\setlength{\bubblesep}{2pt}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\newcommand{\bubble}[2][]{%
\tcbox[
on line,
arc=2.5mm,
rounded corners,
title=#1,halign title=right
]{\begin{varwidth}{\bubblewidth}#2\end{varwidth}}%
}
\newcommand{\bubbledual}[3][]{%
\tcbox[
on line,
arc=2.5mm,
rounded corners,
title=#1,halign title=right
]{\begin{varwidth}{\bubblewidth}#2\end{varwidth}}%
\tcblower
{\begin{varwidth}{\bubblewidth}#3\end{varwidth}}%
}
\ExplSyntaxOn
\seq_new:N \l__itt_bubbles_seq
\tl_new:N \l__itt_bubbles_first_tl
\tl_new:N \l__itt_bubbles_last_tl
\NewEnviron{rightbubbles}[1][]
{
\begin{flushright}
\sffamily
\seq_set_split:NnV \l__itt_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__itt_bubbles_seq < 2 }
{
\bubble[#1]{\BODY}\par
}
{
\seq_map_inline:Nn \l__itt_bubbles_seq
{
\bubble[#1]{##1}
\par\nointerlineskip
\addvspace{\bubblesep}
}
}
\end{flushright}
}
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}[ member A ]
20:20:11" 26 nov. 2020
Right-aligned gray bubbles with black text
20:23:12" 26 nov. 2020
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
20:27:54" 26 nov. 2020
Left and right edges are round.
20:30:06" 26 nov. 2020
Single line ...
\end{rightbubbles}
\end{document}
答案1
虽然迟到了,但是这里有一种方法可以做到:用 拆分输入\regex_split:nnN
;这也可以处理空消息和多条消息行。
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{varwidth}
%\usepackage{xparse} % only needed for LaTeX prior to 2020-10-01
\newlength{\bubblesep}
\newlength{\bubblewidth}
\setlength{\bubblesep}{2pt}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\ExplSyntaxOn
\seq_new:N \l__itt_bubbles_seq
\tl_new:N \l__itt_bubbles_tl
\tl_new:N \l__itt_bubbles_title_tl
\dim_new:N \l__itt_bubbles_width_dim
\box_new:N \l__itt_bubbles_title_box
\box_new:N \l__itt_bubbles_upper_box
\box_new:N \l__itt_bubbles_lower_box
\NewDocumentEnvironment{rightbubbles}{O{} +b}
{
\itt_bubbles:nnn { flushright } { #1 } { #2 }
}
{}
\NewDocumentEnvironment{leftbubbles}{O{} +b}
{
\itt_bubbles:nnn { flushleft } { #1 } { #2 }
}
{}
\cs_new_protected:Nn \itt_bubbles:nnn
{
\begin{#1}
\sffamily
\tl_set:Nn \l__itt_bubbles_title_tl { #2 }
\tl_clear:N \l__itt_bubbles_tl
\regex_split:nnN { (\d\d:\d\d:\d\d\".*?\c{\par}) } { #3 } \l__itt_bubbles_seq
\seq_pop_left:NN \l__itt_bubbles_seq \l_tmpa_tl % discard the first empty item
\seq_map_indexed_function:NN \l__itt_bubbles_seq \__itt_bubbles_prepare:nn
\tl_use:N \l__itt_bubbles_tl
\end{#1}
}
\cs_new_protected:Nn \__itt_bubbles_prepare:nn
{
\int_if_odd:nTF { #1 }
{% start a bubble % title and top
\tl_put_right:Nn \l__itt_bubbles_tl
{
\__itt_bubbles_make:Vnn \l__itt_bubbles_title_tl { #2 }
}
}
{
\tl_put_right:Nn \l__itt_bubbles_tl { { #2 } }
}
}
\cs_new_protected:Nn \__itt_bubbles_make:nnn
{
\hbox_set:Nn \l__itt_bubbles_title_box { #1 }
\hbox_set:Nn \l__itt_bubbles_upper_box { \begin{varwidth}{\bubblewidth}#2\end{varwidth}}
\hbox_set:Nn \l__itt_bubbles_lower_box { \begin{varwidth}{\bubblewidth}#3\end{varwidth}}
\dim_set:Nn \l__itt_bubbles_width_dim
{
\dim_max:nn { \box_wd:N \l__itt_bubbles_title_box } { \box_wd:N \l__itt_bubbles_upper_box }
}
\dim_set:Nn \l__itt_bubbles_width_dim
{
\dim_max:nn { \l__itt_bubbles_width_dim } { \box_wd:N \l__itt_bubbles_lower_box }
}
\begin{tcolorbox}[
left=1em,right=1em,
width=\dim_eval:n { \l__itt_bubbles_width_dim + 3em },
on~line,
arc=2.5mm,
rounded~corners,
halign~title=right,
title=\box_use:N \l__itt_bubbles_title_box,
]
\box_use:N \l__itt_bubbles_upper_box
\tcblower
\box_use:N \l__itt_bubbles_lower_box
\end{tcolorbox}
\par
}
\cs_generate_variant:Nn \__itt_bubbles_make:nnn { V }
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}[member A]
20:20:11" 26 nov. 2020
Right-aligned gray bubbles with black text
20:23:12" 26 nov. 2020
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
20:27:54" 26 nov. 2020
Left and right edges are round.
Another paragraph.
20:30:06" 26 nov. 2020
20:30:06" 26 nov. 2020
Single line ...
\end{rightbubbles}
\begin{leftbubbles}[member B]
20:20:11" 26 nov. 2020
Right-aligned gray bubbles with black text
20:23:12" 26 nov. 2020
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
\end{leftbubbles}
\end{document}
答案2
这做了类似的事情。(我更喜欢更简洁、传统的\def
s ,但expl3
如果需要,您可以将其转换为 。)此代码附带了气泡框的变体,允许使用\tcblower
。
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{varwidth}
\usepackage{environ}
\usepackage{xparse}
\newlength{\bubblesep}
\newlength{\bubblewidth}
\setlength{\bubblesep}{2pt}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\newcommand{\bubble}[2][]{%
\tcbox[
on line,
arc=2.5mm,
rounded corners,
title=#1,halign title=right
]{\begin{varwidth}{\bubblewidth}#2\end{varwidth}}%
}
\newcommand{\bubbledual}[3][]{%
\setbox0\hbox{\begin{varwidth}{\bubblewidth}
\small #2
\end{varwidth}}%
\setbox1\hbox{\begin{varwidth}{\bubblewidth}
#3
\end{varwidth}}%
\ifdim\wd0>\wd1\relax
\edef\mybubblewidth{\the\dimexpr\wd0+1.2cm}%
\else
\edef\mybubblewidth{\the\dimexpr\wd1+1.2cm}%
\fi
\begin{tcolorbox}[width=\mybubblewidth,
arc=2.5mm,
rounded corners,
title=#1,halign title=right
]
\begin{varwidth}{\bubblewidth}
{\small #2}
\end{varwidth}%
\tcblower
\begin{varwidth}{\bubblewidth}
#3
\end{varwidth}%
\end{tcolorbox}}
\def\bubblesplit#1\newline#2\newline{\ifx#2\relax\relax
\bubble[\mytitle]{#1}%
\else
\bubbledual[\mytitle]{#1}{#2}%
\fi
}%
\ExplSyntaxOn
\seq_new:N \l__itt_bubbles_seq
\tl_new:N \l__itt_bubbles_first_tl
\tl_new:N \l__itt_bubbles_last_tl
\NewEnviron{rightbubbles}[1][]
{
\begin{flushright}
\sffamily
\seq_set_split:NnV \l__itt_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__itt_bubbles_seq < 2 }
{
%\bubble[#1]{\BODY}\par
\def\mytitle{#1}%
\let\oldnewline\newline
\let\newline\relax
\expandafter\bubblesplit##1\newline\newline
\let\newline\oldnewline
}
{
\seq_map_inline:Nn \l__itt_bubbles_seq
{
%\bubble[#1]{##1}
\def\mytitle{#1}%
\let\oldnewline\newline
\let\newline\relax
\expandafter\bubblesplit##1\newline\newline
\let\newline\oldnewline
\par\nointerlineskip
\addvspace{\bubblesep}
}
}
\end{flushright}
}
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}[ member A ]
20:20:11" 26 nov. 2020\newline
Right-aligned gray bubbles with black text
20:23:12" 26 nov. 2020\newline
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
20:27:54" 26 nov. 2020\newline
Left and right edges are round.
20:30:06" 26 nov. 2020\newline
Single line \dots
a line without time stamp
\end{rightbubbles}
\end{document}