3

Consider the following minimal working example:

\documentclass{article}

\usepackage[framemethod=TikZ]{mdframed} % noteboxes, boxes, etc.

\newcounter{definition}
\newenvironment{definition}[1][]{%
\stepcounter{definition}%
\ifstrempty{#1}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut Definition~\thedefinition};}}
}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut Definition~\thedefinition:~#1};}}%
}%
\mdfsetup{innertopmargin=1pt,linecolor=red!60,%
    linewidth=2pt,topline=true,
    frametitleaboveskip=\dimexpr-\ht\strutbox\relax,}
\begin{mdframed}[]\relax%
}{\end{mdframed}}

\newcounter{theorem}
\newenvironment{theorem}[1][]{%
\stepcounter{theorem}%
\ifstrempty{#1}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut Theorem~\thetheorem};}}
}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut Theorem~\thetheorem:~#1};}}%
}%
\mdfsetup{innertopmargin=1pt,linecolor=black!60,%
    linewidth=2pt,topline=true,
    frametitleaboveskip=\dimexpr-\ht\strutbox\relax,}
\begin{mdframed}[]\relax%
}{\end{mdframed}}


\begin{document}
    \section{Cats}
        Warm kitty, sweet kitty, little ball of fur. Warm kitty, sweet kitty, purr, purr, purr.

    \begin{definition}[Kitten.]
        A kitten is a baby cat.
    \end{definition}

    \begin{theorem}[Kittens are warm.]
        All kittens are warm.
    \end{theorem}
\end{document}

It produces the following output:

enter image description here

I would like to define some other boxes like this, e.g. axiom:

\newcounter{axiom}
\newenvironment{axiom}[1][]{%
\stepcounter{axiom}%
\ifstrempty{#1}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut Axiom~\theaxiom;}}
}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut Axiom~\theaxiom:~#1};}}%
}%
\mdfsetup{innertopmargin=1pt,linecolor=blue!60,%
    linewidth=2pt,topline=true,
    frametitleaboveskip=\dimexpr-\ht\strutbox\relax,}
\begin{mdframed}[]\relax%
}{\end{mdframed}}

Note that this is a total copy of the other box definitions, except for colour, and what the box "type" is (e.g. axiom, vs. definition or theorem).

So, it seems to me that instead of copy and pasting this style multiple times to create new boxes, I should somehow put the definition into a macro. So I decide to stuff the definition into a \newcommand as follows:

\newcommand{\defcustombox}[2]{%
\newenvironment{\expandafter\Makelowercase\expandafter{#1}}[1][]{%
\stepcounter{\expandafter\Makelowercase\expandafter{#1}}%
\ifstrempty{\#1}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut #1~\the<thing>};}}
}%
{\mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut #1~\the<thing>:~##1};}}%
}%
\mdfsetup{innertopmargin=1pt,linecolor=#2!60,%
    linewidth=2pt,topline=true,
    frametitleaboveskip=\dimexpr-\ht\strutbox\relax,}
\begin{mdframed}[]\relax%
}{\end{mdframed}}
}

\defcustombox{Axiom}{blue} %example of expected usage

For this attempt, these are the issues that I know of (it does not compile, if you try it out):

  • the "content" of the macro has to be output verbatim (note that I have not escaped any of the special characters within the body of the macro); I did attempt to manually escape everything, but it is error-prone and doesn't seem to be "the right way"
  • nested parameters have to be distinguished! (I think the solution to this is to put ##1 when referencing the first argument of the macro being created in the box? I use that to distinguish the inner command's argument)
  • have to come up with an elegant way of expanding \the<thing> based on the first argument of the \defcustombox macro --- will it look something like \\the\expandafter\MakeLowercase\expandafter{#1}?

What should one do to make this work?

  • You can use the \mdfdefinestyle macro (see p.4, section 5, mdframed manual). – Paul Gaborit Feb 8 '18 at 7:19
3

Like this:

\documentclass{article}

\usepackage[framemethod=TikZ]{mdframed} % noteboxes, boxes, etc.

\newcommand{\definebox}[2]{%
  \newcounter{#1}
  \newenvironment{#1}[1][]{%
    \stepcounter{#1}%
    \mdfsetup{%
        frametitle={%
            \tikz[baseline=(current bounding box.east),outer sep=0pt]
            \node[anchor=east,rectangle,fill=white]
            {\strut \MakeUppercase#1~\csname the#1\endcsname\ifstrempty{##1}{}{:~##1}};}}%
    \mdfsetup{innertopmargin=1pt,linecolor=#2,%
        linewidth=2pt,topline=true,
        frametitleaboveskip=\dimexpr-\ht\strutbox\relax,}%
    \begin{mdframed}[]\relax%
    }{\end{mdframed}}%
}

\definebox{definition}{red!60}
\definebox{theorem}{black}

\begin{document}
    \section{Cats}
        Warm kitty, sweet kitty, little ball of fur. Warm kitty, sweet kitty, purr, purr, purr.

    \begin{definition}[Kitten.]
        A kitten is a baby cat.
    \end{definition}

    \begin{theorem}[Kittens are warm.]
        All kittens are warm.
    \end{theorem}
\end{document}
  • Thanks for your answer. Could you also show an example using mdframed's mdfdefinestyle? It's not obvious to me, looking at the documentation, how it could work for this example. How would the counter be defined? Wouldn't the \the<thing> issue still have to be handled? – user89 Feb 8 '18 at 15:04
  • You are right. Sorry, my comment was misleading. The question how to reuse code remains the same, even if you use \mdfdefinestyle, and you also had to use \csname etc. At least I cannot currently think of any simpler approach. – Jasper Habicht Feb 8 '18 at 15:18
  • I didn't notice this until it became relevant, but the numbers based on the counters aren't showing up properly. I think it's an issue with \csname the #1\endcsname, which is confusing since it should be right based on: tex.stackexchange.com/questions/39380/… – user89 Feb 8 '18 at 22:07
  • @user89 Indeed! Sorry, this is due to the space between the and #1. Remove it and it should work. – Jasper Habicht Feb 8 '18 at 22:32
  • Ah yeah, you're right! Also, there should be no space between #1 and \endcsname (I had that, and even if there is no space between the and #1, it still wouldn't work). Why is space even treated as significant within the \csname block? – user89 Feb 8 '18 at 22:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.