I am trying to create a command that the user can enter keys for values. How can I create one, for example:
\myparbox[width=50,height=10,color=blue, align=left -10px]{}
key-valuemacros
I am trying to create a command that the user can enter keys for values. How can I create one, for example:
\myparbox[width=50,height=10,color=blue, align=left -10px]{}
My personal preference is to avoid using .store in
keys at all, with the following exception:
A
.store in
key is required when usingpgfkeys
as an interface to an existing system that doesn't use it.
For example, suppose that you wanted, in the course of your key-processing, to alter the material that would be printed by \ref
if it pointed to a \label
that was called right afterward, i.e.
\pgfkeys{ref text = xyz}
\label{after key}
\ref{after key}% Prints "xyz"
This material is stored (in some fashion) using the \@currentlabel
macro, which is a LaTeX2e kernel internal macro. Thus, you could achieve this effect by declaring ref text
as something like
\pgfkeys{ref text/.store in = \@currentlabel}
Other than this, I think it's a waste of effort to concoct a personal namespace, have to slog through its obfuscation, and in the end duplicate the action of \pgfkeysvalueof
. There is one disadvantage to using .initial
keys together with this macro: although it expands to \pgfk@<key>
, it does so after two layers of indirection: first, expanding \pgfkeysvalueof
, and second, expanding the resulting \csname
, only after which does the actual macro name hit the input stream. I.e., it looks like
\pgfkeysvalueof{<key>}
-> \csname pgfk@<key>\endcsname
-> \pgfk@<key>
This makes it really awkward to manipulate this key as a macro; e.g. if you wanted to make a shortcut, say \let\mymacro<whatever underlies "key">
, you can't do
% Makes \mymacro = \pgfkeysvalueof
\let\mymacro\pgfkeysvalueof{<key>}
nor can you do the usual (ugly) workaround
% Makes \mymacro = \csname
\expandafter\let\expandafter\mymacro\pgfkeysvalueof{<key>}
but you either have to do two runs of \expandafter
:
\expandafter\expandafter\expandafter\let
\expandafter\expandafter\expandafter\mymacro
\pgfkeysvalueof{<key>}
(which is bad in so many ways, first among them that you shouldn't have to know how \pgfkeysvalueof
expands), or you have to just know that the macro is actually just \pgfk@<key>
(which is bad because it's exposing the internal representation of an abstraction).
But you don't want to do this; if you do, then you are misusing pgfkeys
. Instead, if you work within the system you can use .get
:
\pgfkeys{<key>/.get = \mymacro}
which does the last thing mentioned above (but now it's good because it's pgfkeys
itself that uses its own internal representations). I don't think there's a quick way to copy one key to another key, however; the .link
handler functions like \def
rather than \let
, while the \pgfkeyslet{<key>}<macro>
macro copies a macro to a key. Here's a handler that does that:
% Uses `etoolbox` for brevity
\pgfkeys{/handlers/.let/.code = {%
\csletcs{pgfk@\pgfkeyscurrentpath}{pgfk@#1}%
\csletcs{pgfk@\pgfkeyscurrentpath/.@cmd}{pgfk@#1/.@cmd}%
}}
Whether or not this is evil is a matter of ego, I suppose, if writing in the /handlers
namespace makes one a pgfkeys
developer.
Finally, there is a related drawback to using .initial + \pgfkeysvalueof
, namely that you can't easily \expandafter
the resulting macro and there really isn't a way around that. That is, if you use .initial
to store some content that acts like a macro with arguments and you want to both use the key as a macro and store the arguments in another macro, you will have trouble. Say, for example, a key that imitates \section
:
\pgfkeys{section key/.initial = \section}
% \optarg is either empty or contains [...] material
\pgfkeysvalueof{section key}\optarg\mandarg
(which is really sort of pointless but serves as an example): this will just not work, because \section
needs its optional argument to look like one. So you would normally write
\expandafter\section\optarg\mandarg
and all would be well. Unfortunately, the same does not work with section key
:
% Actually does nothing at all
\expandafter\pgfkeysvalueof{section key}\optarg\mandarg
As before, sufficiently many \expandafters
would work, but there really does not exist a single operation that jumps over an entire block of code to expand the next token, so you are in for a mess. And yet this all would be easy if you just used .store in
:
\pgfkeys{section key/.store in = \sectionkey}
\pgfkeys{section key = \section}
% No problem
\expandafter\sectionkey\optarg\mandarg
Since pgfkeys
is an object-oriented language, I consider it a feature that it makes it difficult to operate as a macro language even when it does deign to provide an interface to the one it's written in.
Anyway, if you are going to use macros it's clearly more (time) efficient to .store in
them. Since no one has ever really succeeded in hiding the basic nature of TeX from all programming activities, it's always necessary at some point to do something dirty, and the question is when. I believe these are the only times one would need to actually leave the pgfkeys
walled garden.
Best Answer
Use
pgfkeys
! There are three steps to this: first, you must make your command accept keys as options. Traditionally, this is as you wrote it: an optional argument. In that case, you should start your definition like this:The syntax is that the optional argument is expected to contain a list of keys (possibly set to values) that are then passed to
\pgfkeys
for processing. The second step is to figure out what you're going to do with the results: that is, you imagine that\pgfkeys
does some kind of magic and produces a bunch of macros, or conditionals, and you need to make these things affect the operation of\myparbox
.Let's take the easy ones as an example:
width
andheight
. You will probably just pass them to\parbox
as the optional parameters that control the width and height, and a good way to do that is to store their values in a macro. Let's say thatwidth
goes to the macro\myparboxWidth
andheight
goes to\myparboxHeight
. Then your definition of\myparbox
will look more like:I had to write
[t]
for the first optional argument in\parbox
, which specifies the position in the surrounding text, because height is the second argument. This suggests that we ought to have aposition
key as well that corresponds to a macro\myparboxPosition
. There's a third optional argument that I didn't give, but it's the "inner position", which can be either top, bottom, centered, or stretched. Might as well have aninner position
key that sets\myparboxInnerPos
. That gives:That's enough for now. In order to make this work, you have to define your keys, and that's where
pgfkeys
is far, far better than its competitors. You can tell it to do all sorts of things with the values other than just storing them, though forheight
andwidth
that will be enough. You define keys by using\pgfkeys
in the preamble to set things up:This has a few features. The real action is that I've said that both of these keys will "pass through" their arguments to the respective macros; if you said "width = 10pt" in the options to
\myparbox
, then\myparboxWidth
would get set to10pt
. I wrote.estore in
rather than plain.store in
to force the value to be expanded before being saved; this prevents subtle errors if someone passes a macro that could get changed somehow before being used in\myparbox
.The other feature is that I've put the keys in a family, called
/myparbox
. Inpgfkeys
jargon, this is a "directory", like in a file system. Calling the/myparbox
key changes directory to this one, and then all keys are private to that directory. This prevents name clashes with the (very common) key nameswidth
andheight
. Now you have to modify your\pgfkeys
call in\myparbox
as well to change directory:For the position arguments, it would be nice if they could have more...logical names than simply "t", "b", "c", or "s". Since
pgfkeys
is, at heart, a lookup engine, it is pretty easy to have it map logical names to various actions: you just make each name a key that points to the corresponding action. I would do the following:This is much more intricate than
width
andheight
, so I'll take it apart.First, we have the basic
position
andinner position
keys, which are passed values.\pgfkeys
treats these keys like macros with one argument, so the value is available as#1
. We tell them to store the values in the appropriate place. The/.style
suffix is a "handler" that defines a more complex behavior for a key than just setting a value; in this case, it makes the key "expand" to other keys that are then called to continue the work.What gets stored, though, has to be properly formatted:
\parbox
expects those one-character options and not words. So we define apositions
subdirectory containing all the words we want to accept, defined to contain their translations into\parbox
-speak. (As before, we isolate these special keys in a directory where they can't be seen and won't conflict with real options.) The/.initial
handler sets values for keys the first time they are seen (these keys will never be redefined, actually).Back in
position
andinner position
, the way we actually store the values is by using the/.get
handler for the appropriatepositions/
subkey. What this does is simply copy the value in that key into the named macro, which is what we wanted:position = top
becomes\def\myparboxPosition{t}
(effectively).There is one more complication to take care of: what happens if you only specify half the options? The remaining macros
\myparboxWhatever
will be undefined or, more insidiously, defined to be whatever they got set to the last time they called\myparbox
. We need to establish some defaults. The easiest way of doing that is to make adefault
style key that we run before processing the options in\myparbox
. It may look like this:Then the
\pgfkeys
call in\myparbox
becomesHere is the final result:
Using these techniques, you can (perhaps not easily at first) craft your own options and make them tweak the behavior of
\myparbox
. For example, if you wanted to have acolor
option, you would link it to the argument of a\textcolor
command.