0

I'm defining a tcolorbox environment with xparse library.

\documentclass{article}
\usepackage{tcolorbox}
\tcbuselibrary{xparse}
\DeclareTColorBox[auto counter, number within=section]{mybox}{g}
  {title={mybox \thetcbcounter}, IfValueT={#1}{label={mybox:#1}}}
\begin{document}
\section{test}
\begin{mybox}{}
  \ref{mybox:}
\end{mybox}
\begin{mybox}{label}
  \ref{mybox:label}
\end{mybox}
\end{document}

enter image description here

I want this box to have a label with a prefix mybox:. If I write

\begin{mybox}
  test
\end{mybox}

there won't be a label. However If I write

\begin{mybox}{}
  test
\end{mybox}

there will be a label named mybox: like the mwe above. If there are several environments like this, there will be a Multiply-defined label warning.

How can I make a blank argument {} works the same as no argument?

2
  • Optional arguments use square brackets. A missing {} argument simply grabs whatever token is next (such as t from text). Also, I could not find a {g} argument in xparse. Commented May 24, 2022 at 16:06
  • @JohnKormylo it's in sec 1.8 Backwards Compatibility.
    – Syvshc
    Commented May 24, 2022 at 16:53

3 Answers 3

1

Don't use the g argument type: it's deprecated.

Just use a standard optional argument:

\documentclass{article}
\usepackage{tcolorbox}
\tcbuselibrary{xparse}

\NewTColorBox[auto counter, number within=section]{mybox}{o}
  {
   title={mybox \thetcbcounter},
   IfValueT={#1}{label={mybox:#1}},
  }

\begin{document}

\section{test}

\begin{mybox}
  test
\end{mybox}

\begin{mybox}[label]
  \ref{mybox:label}
\end{mybox}

\end{document}

You gain nothing from being able to code the first as

\begin{mybox}{}
  test
\end{mybox}

However, if you really insist, the code can be

\documentclass{article}
\usepackage{tcolorbox}
\tcbuselibrary{xparse}

\NewTColorBox[auto counter, number within=section]{mybox}{g}
  {
   title={mybox \thetcbcounter},
   IfValueT={#1}{mylabel=#1},
  }
\tcbset{
  mylabel/.code={
    \IfBlankTF{#1}{}{\tcbset{label={mybox:#1}}}%
  }
}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\IfBlankTF}{mmm}
  {
    \tl_if_blank:nTF { #1 } { #2 } { #3 }
  }
\ExplSyntaxOff

\begin{document}

\section{test}

\begin{mybox}{}
  test
\end{mybox}

\begin{mybox}
  test
\end{mybox}

\begin{mybox}{label}
  \ref{mybox:label}
\end{mybox}

\end{document}

The .aux file will have

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}test}{1}{}\protected@file@percent }
\newlabel{mybox:label}{{1.3}{1}}
\gdef \@abspage@last{1}

so no label for the empty argument.

Anyway, the first approach is preferable. Really.

Note also \NewTColorBox, because \DeclareTColorBox would silently override an existing environment with the same name.

1

xparse distinguishes the value of an optional argument when it's missing from when it's given an empty value, while \newcommand and its friends don't.

\IfValueTF{<arg>}{<true>}{<false>} checks if <arg> is missing (having value -NoValue- (-11N11o11V11a11l11u11e11-12, note the special catcode of the first -). expl3's \c_novalue_tl and \tl_if_novalue:nTF play a similar role.

To check if an argument is empty, \ifstrempty from etoolbox fits the requirement. Since tcolorbox already loads etoolbox, there's no need to load it twice.

Imitating how tcolorbox provides options IfValue(TF) as wrappers of \IfValue(TF), here's an example to provide wrapper for \ifstrempty (with argument type g replaced with o):

% tcbxparse.code.tex
\tcbset{
  IfNoValueTF/.code n args={3}{\IfNoValueTF{#1}{\pgfkeysalso{#2}}{\pgfkeysalso{#3}}},
  IfNoValueT/.code n args={2}{\IfNoValueT{#1}{\pgfkeysalso{#2}}},
  IfNoValueF/.code n args={2}{\IfNoValueF{#1}{\pgfkeysalso{#2}}},
  IfValueTF/.code n args={3}{\IfValueTF{#1}{\pgfkeysalso{#2}}{\pgfkeysalso{#3}}},
  IfValueT/.code n args={2}{\IfValueT{#1}{\pgfkeysalso{#2}}},
  IfValueF/.code n args={2}{\IfValueF{#1}{\pgfkeysalso{#2}}},
}
\documentclass{article}
\usepackage{tcolorbox}
\tcbuselibrary{xparse}

\tcbset{
  ifstrempty/.code n args={3}{\ifstrempty{#1}{\pgfkeysalso{#2}}{\pgfkeysalso{#3}}},
}

\DeclareTColorBox[auto counter, number within=section]{mybox}{ o }
  {
    title={mybox \thetcbcounter},
    % add label only if #1 has value and is non empty
    IfNoValueTF={#1}{}{ifstrempty={#1}{}{label={mybox:#1}}}
  }

\begin{document}
\section{test}
\begin{mybox}
\end{mybox}

\begin{mybox}[]
\end{mybox}

\begin{mybox}[label]
\end{mybox}

\ref{mybox:}, \ref{mybox:label}
\end{document}

enter image description here

Hmm the question title asks for "how to determine if ... is blank". Blank means either empty (\mybox[]) or space(s) only (\mybox[ ]). You can define a wrapper for \ifblank similarly.

1
  • Thanks for your answer, I mistook blank for empty,sorry.
    – Syvshc
    Commented May 25, 2022 at 5:43
0

I can define a new tcolorbox key to meet my demand.

\documentclass{article}
\usepackage{etoolbox}
\usepackage{tcolorbox}
\tcbuselibrary{xparse}
\tcbset{
  mylabel/.code n args={1}
    {
      \def\temp{#1}
      \ifdefempty{\temp}{}{
        \tcbset{label={mybox:#1}}
        }
    }
}
\DeclareTColorBox[auto counter, number within=section]{mybox}{g}
  {title={mybox \thetcbcounter}, IfValueT={#1}{mylabel={#1}}}
\begin{document}
\section{test}
\begin{mybox}{}
  test
\end{mybox}
\begin{mybox}
  test
\end{mybox}
\begin{mybox}{label}
  \ref{mybox:label}
\end{mybox}
\end{document}

I can do the empty judgment in the new key mylabel, and the problem is solved.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .