How to make every line in a bubble like chat conversations?
With tcolorbox
:
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{xcolor}
\usepackage{varwidth}
\usepackage{environ}
\usepackage{xparse}
\newlength{\bubblewidth}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\definecolor{bubblegreen}{RGB}{103,184,104}
\definecolor{bubblegray}{RGB}{241,240,240}
\newcommand{\bubble}[4]{%
\tcbox[
colback=#1,
colframe=#1,
#2,
]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}%
}
\ExplSyntaxOn
\seq_new:N \l__ooker_bubbles_seq
\tl_new:N \l__ooker_bubbles_first_tl
\tl_new:N \l__ooker_bubbles_last_tl
\NewEnviron{rightbubbles}
{
\raggedleft\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegreen}{rounded~corners}{white}{\BODY}
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegreen}{sharp~corners=southeast}{white}{\l__ooker_bubbles_first_tl}\par
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegreen}{sharp~corners=east}{white}{##1}\par
}
\bubble{bubblegreen}{sharp~corners=northeast}{white}{\l__ooker_bubbles_last_tl}\par
}
}
\NewEnviron{leftbubbles}
{
\raggedright\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegray}{rounded~corners}{black}{\BODY}
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegray}{sharp~corners=southwest}{black}{\l__ooker_bubbles_first_tl}\par
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegray}{sharp~corners=west}{black}{##1}\par
}
\bubble{bubblegray}{sharp~corners=northwest}{black}{\l__ooker_bubbles_last_tl}\par
}
}
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{rightbubbles}
\begin{leftbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{leftbubbles}
\begin{rightbubbles}
Single
\end{rightbubbles}
\begin{leftbubbles}
End
\end{leftbubbles}
\end{document}
If I change the definition of \bubble
to
\newcommand{\bubble}[4]{%
\tcbox[
arc=5mm,
colback=#1,
colframe=#1,
#2,
]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}%
}
then the output is as follows:
Improved version
You can define the spacing between bubbles by setting \bubblesep
. Different color bubbles are enclosed in flushleft
and flushright
environments, so there will be \topsep
space between them.
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{xcolor}
\usepackage{varwidth}
\usepackage{environ}
\usepackage{xparse}
\newlength{\bubblesep}
\newlength{\bubblewidth}
\setlength{\bubblesep}{2pt}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\definecolor{bubblegreen}{RGB}{103,184,104}
\definecolor{bubblegray}{RGB}{241,240,240}
\newcommand{\bubble}[4]{%
\tcbox[
on line,
arc=4.5mm,
colback=#1,
colframe=#1,
#2,
]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}%
}
\ExplSyntaxOn
\seq_new:N \l__ooker_bubbles_seq
\tl_new:N \l__ooker_bubbles_first_tl
\tl_new:N \l__ooker_bubbles_last_tl
\NewEnviron{rightbubbles}
{
\begin{flushright}
\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegreen}{rounded~corners}{white}{\BODY}\par
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegreen}{sharp~corners=southeast}{white}{\l__ooker_bubbles_first_tl}
\par\nointerlineskip
\addvspace{\bubblesep}
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegreen}{sharp~corners=east}{white}{##1}
\par\nointerlineskip
\addvspace{\bubblesep}
}
\bubble{bubblegreen}{sharp~corners=northeast}{white}{\l__ooker_bubbles_last_tl}
\par
}
\end{flushright}
}
\NewEnviron{leftbubbles}
{
\begin{flushleft}
\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegray}{rounded~corners}{black}{\BODY}\par
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegray}{sharp~corners=southwest}{black}{\l__ooker_bubbles_first_tl}
\par\nointerlineskip
\addvspace{\bubblesep}
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegray}{sharp~corners=west}{black}{##1}
\par\nointerlineskip
\addvspace{\bubblesep}
}
\bubble{bubblegray}{sharp~corners=northwest}{black}{\l__ooker_bubbles_last_tl}\par
}
\end{flushleft}
}
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{rightbubbles}
\begin{leftbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{leftbubbles}
\begin{rightbubbles}
Single
\end{rightbubbles}
\begin{leftbubbles}
End
\end{leftbubbles}
\end{document}
In plain TeX (using pdfTeX) we can do this:
\font\ssf=cmssdc10
\def\Black{\pdfliteral{0 g}}
\def\Blue{\pdfliteral{0 0 1 rg}}
\def\White{\pdfliteral{1 g}}
\def\bptopt#1{.9963 0 0 .9963 0 0 cm 17 0 0 #117 0 3.5 cm }
\def\circle{.5 0 m .5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c
-.5 -.276 -.276 -.5 0 -.5 c .276 -.5 .5 -.276 .5 0 c }
\def\hcircle{.5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c }
\def\fullround{\pdfliteral{q \bptopt+ \circle f Q}}
\def\halfround{%
\ifnum\count255=13
\pdfliteral{q \bptopt+ -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\else \ifnum\count255=14
\pdfliteral{q \bptopt- -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\else
\pdfliteral{q \bptopt+ -.5 -.5 m -.5 .5 l .5 .5 l .5 -.5 l h f Q}%
\fi\fi
}
\def\xround#1#2{\if #1#2\halfround \else \fullround\fi}
\def\messenger#1#2{\medskip\setbox0=\vbox{\penalty13
\widowpenalty=14 \clubpenalty=0 \interlinepenalty=0
\def\\{\unskip\break}\rightskip=0pt plus 1fil\parindent=0pt \ssf #2\par
\setbox1=\box2
\loop \setbox1=\lastbox
\unless\ifvoid1
\unskip\unskip \count255=\lastpenalty \unpenalty
\setbox1=\hbox{\unhbox1\unskip\unskip}
\setbox1=\hbox to\hsize{\hss\Blue\xround l#1%
\rlap{\vrule height12pt depth5pt width\wd1 \xround r#1}\White
\box1 \Black \if l#1\hfill\fi}
\global\setbox2=\vbox{\box1 \vskip1pt \unvbox2}
\repeat
}
\unvbox2
\medskip
}
\hsize=9cm
This is test:
\messenger r {
This is the native format from messenger \\
Notice, how the borders of the sides are circular, not straight and the last
notice is here.
}
\messenger l {
Second message \\
is here.
}
\bye
Edit: I modified the code in order to enable to print two variants: left aligned, if \messenger l
is used and right aligned, if \messenger r
is used. As your comment says.
The result:
Edit The specifications from OP were changed after my first result was published here. First, the colour was blue and bubbles were right aligned. Second, bubbles must be left aligned too. The last change of the specification includes a new request for colours and for formatting the paragraphs. I supposed that when there is my code published then slight changes of formatting a paragraphs or changing colours is just simple task for OP. But probably it cannot be expected because OP does not know what does mean \bye
for example. Moreover, egreg presented second solution here based on a specific Expl3 language. He was in advantage because he knew newest specification from OP. I cannot keep only Expl3 solution here, because it is obscure language from my point of view which cannot help to teach the basics TeX principles. So, there is my second solution using TeX primitives and accepting the new specification form OP. I hope that there will be no more specification changes from OP.
\font\ssf=cmssdc10
\def\Black{\pdfliteral{0 g}}
\def\Blue{\pdfliteral{0 0 1 rg}}
\def\White{\pdfliteral{1 g}}
\def\Gray{\pdfliteral{.945 .941 .941 rg}}
\def\Green{\pdfliteral{.404 .722 .408 rg}}
\def\bcolor#1{\if r#1\Green \else \Gray \fi}
\def\fcolor#1{\if r#1\White \else \Black \fi}
\def\bptopt#1{.9963 0 0 .9963 0 0 cm 17 0 0 #117 0 3.5 cm }
\def\circle{.5 0 m .5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c
-.5 -.276 -.276 -.5 0 -.5 c .276 -.5 .5 -.276 .5 0 c }
\def\hcircle{.5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c }
\def\fullround{\pdfliteral{q \bptopt+ \circle f Q}}
\def\halfround{%
\ifcase\count255
\pdfliteral{q \bptopt+ -.5 -.5 m -.5 .5 l .5 .5 l .5 -.5 l h f Q}%
\or \pdfliteral{q \bptopt+ -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\or \pdfliteral{q \bptopt- -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\or \fullround \fi
}
\def\xround#1#2{\ifdim\ht1>12pt
\dimen0=\ht1 \advance\dimen0 by-12pt
\hbox to0pt{\hss\vbox{\xroundA#1#2\kern\dimen0 \xroundA#1#2}%
\rlap{\kern-8.5pt\raise4.5pt\vbox{\hrule width17pt height\dimen0}}\hss}%
\else \xroundA#1#2\fi}
\def\xroundA#1#2{\if #1#2\halfround \else \fullround\fi}
\long\def\messenger#1#2{\medskip\setbox0=\vbox{\penalty13
\widowpenalty=0 \clubpenalty=0 \interlinepenalty=0
\everypar={\setbox0=\lastbox\endgraf\vbox\bgroup
\everypar={\vrule height12pt width0pt}}
\def\par{\ifhmode\endgraf\egroup\fi}
\def\\{\unskip\break}\rightskip=0pt plus 1fil\parindent=0pt \ssf #2\par
\everypar={}\let\par=\endgraf \setbox1=\box2
\loop \setbox1=\lastbox
\unless\ifvoid1
\unskip\unskip
\count255=\ifvoid2 \ifnum\lastpenalty=13 3\else 2\fi
\else \ifnum\lastpenalty=13 1\else 0\fi \fi
\unpenalty
\ifdim\ht1>12pt \else
\setbox0=\vbox{\unvbox1 \lastbox \setbox1=\lastbox
\global\setbox1=\hbox{\unhbox1\unskip\unskip}}
\fi
\setbox1=\hbox to\hsize{\hss\bcolor#1\xround l#1%
\rlap{\vrule height\ht1 depth5pt width\wd1 \xround r#1}%
\fcolor#1\box1 \Black \if l#1\hfill\fi}
\global\setbox2=\vbox{\box1 \vskip2pt \unvbox2}
\repeat
}
\unvbox2
\medskip
}
\hsize=9cm
This is test:
\messenger r {
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
}
\messenger l {
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
}
\messenger r {
Single
}
\messenger l {
End
}
\bye