1

I want to define a key to handle a length, but also have custom settings.

The usage I intend:

  • my length = auto will set a boolean to false and ignore the length
  • my length = half will set a boolean to true and set the length to 0.5\linewidth
  • my length = 20pt will set a boolean to true and set the length to 20pt

So far, so good, but I'd like to use my length = \linewidth or my length = \myotherlength, for example, but using a macro* as a key breaks the .is choice processing.

  • yes, a length in this case :)

This is my code:

\documentclass[12pt]{article}
\usepackage{pgfkeys}

\newif\ifisauto
\isautofalse

\newlength{\mylength}

\pgfkeys{
    /my test/.cd,
    my length/.is choice,
    my length/auto/.code = {\isautotrue},
    my length/half/.code = {\isautofalse\setlength{\mylength}{0.5\linewidth}},
    my length/.unknown/.code = {\isautofalse\setlength{\mylength}{\pgfkeyscurrentname}}%  won't work with macros
}

\newcommand{\printinfo}{%
    \ifisauto
        Choice is auto, ignoring \textbackslash mylength%
    \else
        Choice is not auto and \textbackslash mylength is \the\mylength%
    \fi%
}

\begin{document}

Tests:

\pgfkeys{/my test/my length = auto}%  OK
\printinfo

\pgfkeys{/my test/my length = half}%  OK
\printinfo

\pgfkeys{/my test/my length = 20pt}%  OK
\printinfo

\pgfkeys{/my test/my length = \linewidth}%  this fails!
\printinfo

\end{document}

Any ideas on how to get this working without having separate keys for the boolean and the length?

2
  • If I take the opposite way... Is there a way to check if \pgfkeyscurrentname is a length or simple text? Just thinking about other possibilities...
    – Jander
    Jun 27, 2023 at 15:15
  • Yes. I think I'll just write my own code... :)
    – Jander
    Jun 27, 2023 at 15:45

3 Answers 3

1

We can create this with an altered .is choice handler (I'll call it .is robust choice). The changes compared to current pgfkeys' .is choice are:

  • The .unknown handler can access the unknown value directly via #1.
  • The choice is matched as a string, so macros don't hurt there.
\documentclass[12pt]{article}
\usepackage{pgfkeys}

\newif\ifisauto
\isautofalse

\newlength{\mylength}


\makeatletter
% this is based on the definition of the .is choice handler
\pgfkeys{/handlers/.is robust choice/.code=%
  \pgfkeys{%
    \pgfkeyscurrentpath/.cd,%
    .code=%
      \edef\pgfkeys@was@choice{\unexpanded{##1}}%
      \pgfkeysifdefined{\pgfkeyscurrentkey/\detokenize{##1}/.@cmd}%
        {\expandafter\pgfkeysalso\expandafter{\pgfkeyscurrentkey/\detokenize{##1}}}%
        {\expandafter\pgfkeysalso\expandafter{\pgfkeyscurrentkey/.unknown={##1}}},
    .unknown/.code={% default .unknown handler of an .is choice
      \def\pgf@marshal{\pgfkeysvalueof{/errors/unknown choice value/.@cmd}}%
      {\expandafter\expandafter\expandafter\pgf@marshal\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pgfkeys@pathtoks\expandafter}\expandafter{\pgfkeys@was@choice}\pgfeov}%
    }%
  }%
}
\makeatother

\pgfkeys{
    /my test/.cd,
    my length/.is robust choice,
    my length/auto/.code = {\isautotrue},
    my length/half/.code = {\isautofalse\setlength{\mylength}{0.5\linewidth}},
    my length/.unknown/.code = {\isautofalse\setlength{\mylength}{#1}}%  won't work with macros
}

\newcommand{\printinfo}{%
    \ifisauto
        Choice is auto, ignoring \textbackslash mylength%
    \else
        Choice is not auto and \textbackslash mylength is \the\mylength%
    \fi%
}

\begin{document}

Tests:

\typeout{\string\pgfkeys{/my test/my length = auto}}%  OK
\pgfkeys{/my test/my length = auto}%  OK
\printinfo

\pgfkeys{/my test/my length = half}%  OK
\printinfo

\pgfkeys{/my test/my length = 20pt}%  OK
\printinfo

\pgfkeys{/my test/my length = \linewidth}%  this fails!
\printinfo

\end{document}

enter image description here

1
  • I was working on my own solution, but I'll take a look at this!
    – Jander
    Jun 27, 2023 at 16:56
1

If you don't have to use pgfkeys you could as well use a key=value implementation that comes with the required behaviour built in, like expkv-def. In it you can use a choice and set the unknown handler with unknown-choice. The protected is just there to make the underlying key-code \protected (else you'd get problems with your definitions in \edef or \expanded contexts).

Disclaimer: I'm the author of expkv-def.

\documentclass[12pt]{article}
\usepackage{expkv-def}

\newif\ifisauto
\isautofalse

\newlength{\mylength}

\ekvdefinekeys{my test}
  {
    protected choice my length = {
      auto = \isautotrue,
      half = \isautofalse\setlength\mylength{.5\linewidth}
    },
    protected unknown-choice my length = \isautofalse\setlength\mylength{#1}
  }

\newcommand{\printinfo}{%
    \ifisauto
        Choice is auto, ignoring \textbackslash mylength%
    \else
        Choice is not auto and \textbackslash mylength is \the\mylength%
    \fi%
}

\begin{document}

Tests:

\ekvset{my test}{my length = auto}%  OK
\printinfo

\ekvset{my test}{my length = half}%  OK
\printinfo

\ekvset{my test}{my length = 20pt}%  OK
\printinfo

\ekvset{my test}{my length = \linewidth}%  this fails!
\printinfo

\end{document}

enter image description here

1
  • All the rest of my code uses pgfkeys, but interesting alternatives are good to know
    – Jander
    Jun 27, 2023 at 17:24
0

Since using .is choice seems unfeasible without changing some interal code, the solution ended up being to do the comparisons manually.

\documentclass[12pt]{article}
\usepackage{pgfkeys}

\usepackage{ifthen}

\newif\ifisauto
\isautofalse

\newlength{\mylength}

\pgfkeys{
    /my test/.cd,
    my length/.code = {%
        \ifthenelse{\equal{auto}{#1}}{%
            \isautotrue%
        }{%
            \isautofalse%
            \ifthenelse{\equal{half}{#1}}{%
                \setlength{\mylength}{0.5\linewidth}%
            }{%
                \setlength{\mylength}{#1}%
            }%
        }%
    },
}

\newcommand{\printinfo}{%
    \ifisauto
        Choice is auto, ignoring \textbackslash mylength%
    \else
        Choice is not auto and \textbackslash mylength is \the\mylength%
    \fi%
}

\begin{document}

Tests:

\pgfkeys{/my test/my length = auto}%  OK
\printinfo

\pgfkeys{/my test/my length = half}%  OK
\printinfo

\pgfkeys{/my test/my length = 20pt}%  OK
\printinfo

\pgfkeys{/my test/my length = \linewidth}%  OK now
\printinfo

\pgfkeys{/my test/my length = 0.1\linewidth}%  OK now
\printinfo

\pgfkeys{/my test/my length = \dimexpr \linewidth/3 - 2pt}%  OK now
\printinfo

\end{document}

You must log in to answer this question.

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