0

I am trying to make it so when I define tcolorbox theorem environments, that I dont have to enter two extra curly braces after \begin{environment}{}{}.

If I don't put the curly braces, the first two letters of text within the environment get absorbed within where the curly braces used to be.

The problem might arise from the definition of the tcbtheorem? See MWE below

\documentclass[12pt]{report}
\usepackage[most]{tcolorbox}
\newtcbtheorem[number within = chapter]{dfn}{Definition}%
{
    enhanced,
    before title = {\stepcounter{dfn}},
    colback=blue!10,
    colframe=blue!35!black,
    fonttitle=\bfseries,
    top=3mm,
    attach boxed title to top left={xshift = 5mm, yshift=-1.5mm},
    boxed title style = {colback=blue!35!black}
}{dfn}
\newcounter{dfn}
\begin{document}

\begin{dfn} abc \end{dfn}

\begin{dfn}{}{} abc \end{dfn}

\end{document}

enter image description here

Hushus46
  • 211

2 Answers2

2

An environment defined with \newtcbtheorem indeed requires two mandatory arguments:

\begin{dfn}{}{}
Text of the definition
\end{dfn}

The first argument is for the title, the second is for the label. With a call such as

\begin{dfn}{Nice one}{}
Text of the definition
\end{dfn}

the title would be set to “Nice one” and there would be no label. With

\begin{dfn}{Nice one}{nice}
Text of the definition
\end{dfn}

you can refer to the number of the definition by \ref{dfn:nice} (the prefix before the colon is the last argument for \newtcbtheorem.

You can also leave the title empty and set a label

\begin{dfn}{}{nice}
Text of the definition
\end{dfn}

That's the way it is.


You might define a wrapper around such theorems so you can use a different and, in my opinion, handier syntax.

\documentclass[12pt]{report}
\usepackage[most]{tcolorbox}
\newtcbtheorem[number within = chapter]{dfninner}{Definition}%
 {
    enhanced,
    before title = {\stepcounter{dfn}},
    colback=blue!10,
    colframe=blue!35!black,
    fonttitle=\bfseries,
    top=3mm,
    attach boxed title to top left={xshift = 5mm, yshift=-1.5mm},
    boxed title style = {colback=blue!35!black}
}{dfn}
\newcounter{dfn}
\ExplSyntaxOn
\NewDocumentEnvironment{dfn}{O{}}
 {
  \keys_set:nn { hushus/tcb } { #1 }
  \hushus_tcb_begin:nVV {dfninner} \l__hushus_tcb_title_tl \l__hushus_tcb_label_tl
 }
 {
  \end{dfninner}
 }
\cs_new_protected:Nn \hushus_tcb_begin:nnn
 {
  \begin{#1}{#2}{#3}
 }
\cs_generate_variant:Nn \hushus_tcb_begin:nnn { nVV }
\keys_define:nn { hushus/tcb }
 {
  title .tl_set:N = \l__hushus_tcb_title_tl,
  label .tl_set:N = \l__hushus_tcb_label_tl,
 }
\ExplSyntaxOff

\begin{document}

\begin{dfn} abc \end{dfn}

\begin{dfn}[title=Nice,label=nice] abc \end{dfn}

\ref{dfn:nice}

\end{document}

enter image description here

An abstraction of the previous code that allows to use the standard tcolorbox syntax and takes care of the modifications for all environments you want to define.

\documentclass[12pt]{report}
\usepackage[most]{tcolorbox}

\ExplSyntaxOn

\NewDocumentCommand{\betternewtcbtheorem}{O{}mmmm} { \newtcbtheorem[#1]{#2inner}{#3}{#4}{#5} \NewDocumentEnvironment{#2}{O{}} { \keys_set:nn { hushus/tcb } { ##1 } \hushus_tcb_begin:nVV {#2inner} \l__hushus_tcb_title_tl \l__hushus_tcb_label_tl } { \end{#2inner} } \cs_if_exist:cF { c@#5} { \newcounter{#5} } }

\cs_new_protected:Nn \hushus_tcb_begin:nnn { \begin{#1}{#2}{#3} } \cs_generate_variant:Nn \hushus_tcb_begin:nnn { nVV } \keys_define:nn { hushus/tcb } { title .tl_set:N = \l__hushus_tcb_title_tl, label .tl_set:N = \l__hushus_tcb_label_tl, }

\ExplSyntaxOff

\betternewtcbtheorem[number within = chapter]{dfn}{Definition}% { enhanced, before title = {\stepcounter{dfn}}, colback=blue!10, colframe=blue!35!black, fonttitle=\bfseries, top=3mm, attach boxed title to top left={xshift = 5mm, yshift=-1.5mm}, boxed title style = {colback=blue!35!black} }{dfn}

\begin{document}

\begin{dfn} abc \end{dfn}

\begin{dfn}[title=Nice,label=nice] abc \end{dfn}

\ref{dfn:nice}

\end{document}

What's happening? The idea is that \betternewtcbtheorem defines an “inner” environment that will be called by the one with the desired name.

In the example above, the desired name is dfn, so first we call \newtcolorbox passing the arguments, but with inner appended to the name. Next the dfn environment is defined, taking an optional argument that should contain something with key-value syntax.

The allowed keys are title and label. When dfn is called, we pass the values in the proper place. Since these values are stored in token list variables, I define a function

\hushus_tcb_begin:nnn

that will call \begin{dfninner}{<title>}{<label>}, but I need to pass the contents of two variables, so this is the place for the variant

\hushus_tcb_begin:nVV

that takes the first argument braced as usual, but the other two should be token list variables, whose content is passed to the main function as if they were braced arguments.

Final touch: a counter is defined if not previously existing, based on the last argument to \newtcbtheorem.

egreg
  • 1,121,712
  • I'm assuming, by you're wording, that these arguments can not be made optional and I'm just going to have to make my macro on TeXStudio to include two empty curly braces when spawning the environment? – Hushus46 Feb 04 '21 at 17:51
  • @Hushus46 I added a way to avoid them, so what's optional is really optional. – egreg Feb 04 '21 at 17:57
  • Awesome, if I have multiple environments like this, for theorem corollary etc. do I need to create a separate wrapper for each one with the \NewDocumentEnvironment{(environmentname)} and just keep the core part of it the same? – Hushus46 Feb 04 '21 at 17:59
  • @Hushus46 One might abstract the code. I can be adding something, stay tuned. – egreg Feb 04 '21 at 18:01
  • alright, thanks! – Hushus46 Feb 04 '21 at 18:03
  • @egreg Despite expl3 seems very powerful, I'm still rejected by the syntax… Can you please add some comments? – NBur Feb 04 '21 at 18:03
  • @NBur I'll comment the last part – egreg Feb 04 '21 at 18:09
  • 1
    @Hushus46 New version with the abstraction layer is online. – egreg Feb 04 '21 at 18:16
  • @egreg Wow, that worked perfectly. I'm going to use this in the preamble of all my mathwork from now on. Thanks! – Hushus46 Feb 04 '21 at 18:24
  • @egreg what is the variable name of the counter you define as \newcounter{#5}. I mean, how can I reference or get the counter value, like 1.1 for Theorem 1.1 – Hushus46 Mar 23 '21 at 23:55
  • @Hushus46 I thought you knew, because the counter already is in your code. – egreg Mar 24 '21 at 09:02
  • @egreg I never explicitly defined my own counter, in each \betternewtcbtheorem I had the options [number within=chapter] and the construction \before title = {\stepcounter{thm}} or {\stepcounter{dfn}}, etc. And what you have written constructs a counter if it didn't have one already. But then, I don't know how to call the counter value for theorems or definitions or whatever other environments I have, I thought it would be like \thecounter or \ref{#5} but that doesn't seem to work – Hushus46 Mar 24 '21 at 12:03
2

You can define a new environment with optional arguments, and check if the values are provided.

\documentclass[12pt]{report}
\usepackage{xparse}
\usepackage[most]{tcolorbox}
\newtcbtheorem[number within = chapter]{dfn}{Definition}%
{
    enhanced,
    before title = {\stepcounter{dfn}},
    colback=blue!10,
    colframe=blue!35!black,
    fonttitle=\bfseries,
    top=3mm,
    attach boxed title to top left={xshift = 5mm, yshift=-1.5mm},
    boxed title style = {colback=blue!35!black}
}{dfn}
\newcounter{dfn}
\NewDocumentEnvironment{DFN}{d()d()}{\begin{dfn}{\IfValueT{#1}{#1}}{\IfValueT{#2}{#2}}}{\end{dfn}}

\begin{document}

\begin{dfn}
    abc
\end{dfn}

\begin{DFN} abc \end{DFN}

\begin{dfn}{}{}
    abc
\end{dfn}

\begin{DFN}(q)(s) abc \end{DFN}

\end{document}

NBur
  • 4,326
  • 10
  • 27