Generalxkeyval questions

LaTeX specific issues not fitting into one of the other forums of this category.
Post Reply
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

xkeyval questions

Post by gmedina »

Hi,

I've been doing some experiments with the xkeyval package and I've encountered two problems I haven't been able to solve (yet); in the following example code I define and set two families of keys: box and text and use those to build two simple commands.

1) How do I keep local the values set for the keys in the optional argument of \mybox? (if you compile my example code you'll have a better idea of my problem).

Code: Select all

\documentclass{article}
\usepackage{xkeyval}
\usepackage{xcolor}

\makeatletter
\define@key{box}{fcolor}{\def\FrameColor{#1}}
\define@key{box}{bgcolor}{\def\BgColor{#1}}
\setkeys{box}{fcolor=blue,bgcolor=red}
\define@key{text}{family}{\def\FontFamily{#1}}
\define@key{text}{series}{\def\FontSeries{#1}}
\setkeys{text}{family=\sffamily,series=\bfseries}
\makeatother

\newcommand\mybox[2][]{%
  \setkeys{box}{#1}
  \fcolorbox{\FrameColor}{\BgColor}{#2}
}

\newcommand\mytext[2][]{%
  \setkeys{text}{#1}
  {\FontFamily\FontSeries#2}
}

\begin{document}

This box has (as it should) the values that were set for the keys in the definition
\mybox{text}

This box has (as it should) the values set in the optional argument of \verb+\mybox+
\mybox[fcolor=blue,bgcolor=yellow]{text}

This box should have the value that was set for the keys in the definition
instead, it has the same values that were set in the previous call of \verb+\mybox+
\mybox{text}

\end{document}
2) I'd like to define a command (let's call it \setup) to set all four (or some of the four) keys fcolor, bgcolor, family, series anywhere in my document. Of course, if I set the values for all (or some) of the keys with \setup, then those values must be locally overwritten when \mybox (or \mytex) is invoked with some keys changed via their optional argument. How can I define the \setup commmand?

Edit: I suppressed a superfluous optional argument from my example code (a remanent of the real code I am writing).
Last edited by gmedina on Wed Apr 14, 2010 9:22 pm, edited 1 time in total.
1,1,2,3,5,8,13,21,34,55,89,144,233,...

Recommended reading 2024:

LaTeXguide.org • LaTeX-Cookbook.net • TikZ.org

NEW: TikZ book now 40% off at Amazon.com for a short time.

josephwright
Site Moderator
Posts: 814
Joined: Tue Jul 01, 2008 2:19 pm

xkeyval questions

Post by josephwright »

The answers here are not specific to xkeyval: the same ideas apply to all keyval packages.

For optional keyval-based arguments you need to keep everything inside a group. So you want:

Code: Select all

\newcommand\mybox[2][]{%
  \begingroup
    \setkeys{box}{#1}%
    \fcolorbox{\FrameColor}{\BgColor}{#2}%
  \endgroup
}
to "trap" the settings. (All of the keyval packages do local assignments for this reason amongst others.)

You \setup macro is then just

Code: Select all

\newcommand\setup[1]{%
  \setkeys{box}{#1}%
}
as this will apply based on whatever grouping is currently in force (usually none at all).
Joseph Wright
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

xkeyval questions

Post by gmedina »

First question answered; thank you very much, Joseph! (Apparently it's one of my dumb days).

The second question, however, still bothers me; your \setup command can only process keys for the box family; my idea is to define \setup so that it can receive any combination of keys for both the box and text families and set them appropriately; the silly attempt

Code: Select all

\newcommand\setup[1]{%
  \setkeys{box}{#1}
  \setkeys{text}{#1}
}
will obviously produce errors when using, for example, \setup{fcolor=red} since the key fcolor doesn't belong to the text family. Of course, I could define separate setup commands (one for each family), but I want (if possible) to use only one command.
1,1,2,3,5,8,13,21,34,55,89,144,233,...
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

xkeyval questions

Post by gmedina »

Well, I've found an answer to my second question (but maybe there's another approach). The idea is to define all of the keys for both families; since originally only fcolor and bgcolor were meant to be keys for the box family, then the \define@key command for the other two keys (family and series) simply will execute no action; a similar treatment is applied to the text family.

An example:

Code: Select all

\documentclass{article}
\usepackage{xkeyval}
\usepackage{xcolor}

\newcommand*\setup[1]{%
  \setkeys{box}{#1}
  \setkeys{text}{#1}
}
\makeatletter
\define@key{box}{fcolor}{\def\FrameColor{#1}}
\define@key{box}{bgcolor}{\def\BgColor{#1}}
\define@key{box}{family}{}
\define@key{box}{series}{}
\setkeys{box}{fcolor=blue,bgcolor=red}
\define@key{text}{family}{\def\FontFamily{#1}}
\define@key{text}{series}{\def\FontSeries{#1}}
\define@key{text}{fcolor}{}
\define@key{text}{bgcolor}{}
\setkeys{text}{family=\sffamily,series=\bfseries}
\makeatother

\newcommand\mybox[2][]{%
  \setkeys{box}{#1}
  \fcolorbox{\FrameColor}{\BgColor}{#2}
}

\newcommand\mytext[2][]{%
  \setkeys{text}{#1}
  {\FontFamily\FontSeries#2}
}

\begin{document}

\setup{bgcolor=blue,series=\normalfont}

\mybox{text}\mytext{text}

\end{document}
1,1,2,3,5,8,13,21,34,55,89,144,233,...
josephwright
Site Moderator
Posts: 814
Joined: Tue Jul 01, 2008 2:19 pm

xkeyval questions

Post by josephwright »

xkeyval does have some stuff for setting keys from multiple families, but I always find this rather complex. What's not clear to me is why you need two separate families at all: usually one package => one family. I'd probably do something like:

Code: Select all

\define@key{mybox}{fcolor}{\def\FrameColor{#1}}
\define@key{mybox}{bgcolor}{\def\BgColor{#1}}
\define@key{mybox}{family}{\def\FontFamily{#1}}
\define@key{mybox}{series}{\def\FontSeries{#1}}
\setkeys{mybox}{fcolor=blue,bgcolor=red,family=\sffamily,series=\bfseries}
You might then have a "meta" key which sets two lower-level ones:

Code: Select all

\define@key{mybox}{box-color}{\def\FrameColor{#1}}
\define@key{mybox}{text-color}{\def\TextColor{#1}}
\define@key{mybox}{color}{%
  \setkeys{mybox}{box-color=#1,text-color=#1}%
}
Joseph Wright
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

xkeyval questions

Post by gmedina »

josephwright wrote:...What's not clear to me is why you need two separate families at all: usually one package => one family...
I am defining several commands and two of them have a color key which the user must be able to set independently, so I decided to use two families, one for each command; in fact, since I am defining four independent commands, I used four families of keys, one for each command. Am I "overusing" the purpose of families? Is there a better way to do this?
1,1,2,3,5,8,13,21,34,55,89,144,233,...
josephwright
Site Moderator
Posts: 814
Joined: Tue Jul 01, 2008 2:19 pm

xkeyval questions

Post by josephwright »

gmedina wrote: I am defining several commands and two of them have a color key which the user must be able to set independently, so I decided to use two families, one for each command; in fact, since I am defining four independent commands, I used four families of keys, one for each command. Am I "overusing" the purpose of families? Is there a better way to do this?
This depends on the context, but I'd probably go with one family and multiple keys. This is the approach I've taken in siunitx with numbers and units, which have a number of settings which can apply independently or to only one. So I have options such as "unit-color" and "text-color", plus a combined "color" one that internally sets both "unit-color" and "text-color". (I'm using the names for the options as in the upcoming siunitx v2: v1 has the same concept but somewhat more awkward option names.)

What's not quite clear to me is exactly how you are applying options. If you have a macro

Code: Select all

\mymacro[<options>]{<argument>}
in which <options> sets several different "kinds" of colour, then I'd go with one family and sub-divide by option names. On the other hand, if you are creating several related functions

Code: Select all

\mymacroone[<options-one>]{<argument>}
\mymacrotwo[<options-two>]{<argument>}
\mymacrothree[<options-three>]{<argument>}
then you might be better with one family per macro if the options are always set independently. From what you say, your situation is more like the first scenario, so I'd favour one family.

Perhaps you could explain the full situation in more detail so it's easier to make a judgement. A short example of the user interface (macros + options) you actually are using might be enlightening.
Joseph Wright
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

xkeyval questions

Post by gmedina »

josephwright wrote: ...Perhaps you could explain the full situation in more detail so it's easier to make a judgement. A short example of the user interface (macros + options) you actually are using might be enlightening.
No, there's no need for that. Thanks to your remarks I have clarified my thoughts and my design. I was using the same word "color" to represent two keys controlling certain color attribute for two commands.

The problem was all mine (a bad design) since I was (wrongly) using the same key to control two different color attributes: initially I considered using one family for the "color" key of \commandone and a different family for the "color" key of \commandtwo. Since the first "color" controls a rule color, and the second one controls a background color, the easiest (and, in fact, proper) way to proceed was simply to use only one family and to use "rulecolor" as the key for \commandone, and "bgcolor" as the key for \commandtwo.

Again, thank you very much for your kind help and your useful remarks.
1,1,2,3,5,8,13,21,34,55,89,144,233,...
Post Reply