7

Is it possible to create a key handler with the pgfkeys package that removes a key from a style?

Here's a minimal working example:

\documentclass[preview,margin=10pt,convert]{standalone}
\usepackage{pgfkeys}
\pgfkeys{/a/.code={a#1},
    /b/.code={b#1},
    /c/.code={c#1}}
\pgfkeys{/my style/.style = {/a=1, /b=2, /c=3}}
\begin{document}
\pgfkeys{/my style}\par
%\pgfkeys{/my style/.remove from style = {b}}
\pgfkeys{/my style}
\end{document}

The output is

a1b2c3a1b2c3

The desired output, if the commented line were to be uncommented, would be

a1b2c3
a1c3

Here are a couple of closely related questions. They are not duplicates of this one, but the answers to the second one might be useful in devising an answer to this question.

Unsetting a PGF/TikZ key

How to extract the value from a pgfkeys style element

2 Answers2

2

The following implements this with the help of expkv (as that one is expandable and fast, so I can set up the key-filtering easier).

It utilises the internal structure of a .style, parses the stored key list, and filters the values inside of that key list.

Caveat: You have to provide the key path exactly how it was defined in the style, this solution does no extensive path-parsing.

\documentclass[preview,margin=10pt]{standalone}
\usepackage{pgfkeys}

% how a style is defined (current code 2023-06-17) \iffalse \pgfkeys{/handlers/.style/.code=\pgfkeys{\pgfkeyscurrentpath/.code=\pgfkeysalso{#1}}} \pgfkeysdef{/handlers/.code}{\pgfkeysdef{\pgfkeyscurrentpath}{#1}} \long\def\pgfkeysdef#1#2{% \long\def\pgfkeys@temp##1\pgfeov{#2}% \pgfkeyslet{#1/.@cmd}{\pgfkeys@temp}% \pgfkeyssetvalue{#1/.@body}{#2}% } \long\def\pgfkeyslet#1#2{% \expandafter\let\csname pgfk@#1\endcsname#2% } \fi

\pgfkeys { /a/.code={a#1} ,/b/.code={b#1} ,/c/.code={c#1} ,/my style/.style = {/a=1, /b=2, /c=3} }

\usepackage{expkv} % yes, I use expkv to filter pgfkeys...

\makeatletter \ExplSyntaxOn \cs_new:Npn \mypgfkeys@checkstyle #1 { \bool_lazy_and:nnF { \tl_if_head_eq_meaning_p:nN {#1} \pgfkeysalso } { \exp_args:No \tl_if_single_p:n { \use_none:n #1 } } } \ExplSyntaxOff \protected\def\mypgfkeys@not@a@style {% \PackageError{mypgfkeys}{Not a style: \pgfkeyscurrentpath}{} \mypgfkeys@break } \def\mypgfkeys@breakpoint#1{} \def\mypgfkeys@break#1\mypgfkeys@breakpoint#2{#2} \pgfkeys{/handlers/.remove from style/.code=\mypgfkeysrmfromstyle{#1}} \def\mypgfkeysrmfromstyle@mark{\mypgfkeysrmfromstyle@mark} \protected\def\mypgfkeysrmfromstyle#1% {% \begingroup \ekvcsvloop\mypgfkeysrmfromstyle@makemark{#1}% \expandafter\let\csname mypgfkeys@@#1\endcsname\mypgfkeysrmfromstyle@mark \pgfkeysgetvalue{\pgfkeyscurrentpath/.@body}\mypgfkeysrmfromstyle@body \ifx\mypgfkeysrmfromstyle@body\relax \mypgfkeys@not@a@style \fi \expandafter\mypgfkeys@checkstyle\expandafter {\mypgfkeysrmfromstyle@body}% \mypgfkeys@not@a@style \edef\mypgfkeysrmfromstyle@body {% \expanded {% \expandafter\mypgfkeysrmfromstyle@do\expandafter {\mypgfkeysrmfromstyle@body}% }% }% \expanded{% \endgroup \unexpanded{\pgfkeys}% {% \pgfkeyscurrentpath/.style= {% \unexpanded\expandafter\expandafter\expandafter {\expandafter@gobble\mypgfkeysrmfromstyle@body}% }% }% }% \mypgfkeys@breakpoint\endgroup } \protected\def\mypgfkeysrmfromstyle@makemark#1% {\expandafter\let\csname mypgfkeys@@#1\endcsname\mypgfkeysrmfromstyle@mark} \def\mypgfkeysrmfromstyle@do#1% {\expandafter\mypgfkeysrmfromstyle@do@\expandafter{@secondoftwo#1}} \def\mypgfkeysrmfromstyle@do@ {\ekvparse\mypgfkeysrmfromstyle@do@k\mypgfkeysrmfromstyle@do@kv} \def\mypgfkeysrmfromstyle@do@k#1% {% \expandafter\ifx\csname mypgfkeys@@#1\endcsname\mypgfkeysrmfromstyle@mark \expandafter@gobbletwo \fi \unexpanded{,#1}% } \long\def\mypgfkeysrmfromstyle@do@kv#1#2% {% \expandafter\ifx\csname mypgfkeys@@#1\endcsname\mypgfkeysrmfromstyle@mark \expandafter@gobbletwo \fi \unexpanded{,#1= {#2}}% } \makeatother

%\expandafter\show\csname pgfk@/my style/.@body\endcsname

\begin{document} \pgfkeys{/my style}\par \pgfkeys{/my style/.remove from style={/b,/c}} \pgfkeys{/my style} \end{document}

enter image description here

Skillmon
  • 60,462
1

Depending on your use case you can install a key filter.

This doesn't remove /b from /my style it just disables the /b style when /my style (or any other style) is used within \pgfkeysfiltered.

Code

\documentclass{article}
\usepackage{pgfkeys}
\pgfkeys{
  /a/.code={a#1},
  /b/.code={b#1},
  /c/.code={c#1},
  /my style/.style = {/a=1, /b=2, /c=3},
  /x/.style={/b=3, /c=4},
}
\begin{document}
normal: \pgfkeys{/my style}\par
\pgfkeys{
  /pgf/key filters/not/.install key filter={
    /pgf/key filters/equals=/b
  }
}
filtered: \pgfkeysfiltered{/my style}\par
Add /x \pgfkeys{/my style/.append style=/x}
and filter: \pgfkeysfiltered{/my style}\par
\end{document}

Output

normal: a1b2c3
filtered: a1c3
Add /x and filter: a1c3c4

Qrrbrbirlbel
  • 119,821