改进键盘命令宏

改进键盘命令宏

我的问题是如何通过 keycommand 包改进已经工作的宏。以下小内容展示了我迄今为止的努力:

\documentclass{article}
\usepackage[margin=0.95in]{geometry}
\usepackage[draft,columns=1]{typogrid}
\usepackage{varwidth}
\usepackage{xkeyval}
\usepackage{keycommand}
% patch by Joseph Wright ("bug in the definition of \ifcommandkey (2010/04/27 v3.1415)"),
% https://tex.stackexchange.com/a/35794/
\begingroup
  \makeatletter
  \catcode`\/=8 %
  \@firstofone
    {
      \endgroup
      \renewcommand{\ifcommandkey}[1]{%
        \csname @\expandafter \expandafter \expandafter
        \expandafter \expandafter \expandafter \expandafter
        \kcmd@nbk \commandkey {#1}//{first}{second}//oftwo\endcsname
      }
    }
%=======================%
\usepackage{xcolor}
\usepackage{tikz}
\usetikzlibrary{positioning}

\definecolor{tikzgray}{HTML}{E5E5FF}
\definecolor{tikzyellow}{HTML}{F4F4CC}

\makeatletter
\newkeycommand\TikzBoxx[
    bool draft=false,
    rule=.5in,
    width=.811\textwidth,
    text={no text here},
    scolor=tikzyellow,
    lcolor=tikzgray]
    [1]{%
    \ifcommandkey{\commandkey{draft}}
        {\begin{tikzpicture}[node distance = 0mm and 2mm,box/.style={rectangle,draw}]}
        {\begin{tikzpicture}[node distance = 0mm and 2mm,box/.style={rectangle}]}
    \rule{\commandkey{rule}}{0pt}%
    \node (n1) [box, fill=\commandkey{scolor}] {\commandkey{text}};%
    \node (n2) [box, fill=\commandkey{lcolor}, text width=\commandkey{width}, below right=of n1.north east] {%
        \vbox{\noindent\ #1}
    };%
    \end{tikzpicture}
}
\makeatother

\def \HELLO {Hello World!}
\def \LTEXT {
Each key (may) store some \emph{tokens} and there exist\newline%
commands, described below, for setting, getting, and\newline%
changing the tokens stored in a key. However, you will\newline%
only very seldom use these commands directly.%
}

\begin{document}

\vspace{\baselineskip}
\TikzBox[text=\HELLO]{\LTEXT}

\end{document}

它确实能用,但在我看来,它也太丑了。问题在于,我使用以下方法强行进行了简单的文本替换:

    \ifcommandkey{\commandkey{draft}}
        {\begin{tikzpicture}[node distance = 0mm and 2mm,box/.style={rectangle,draw}]}
        {\begin{tikzpicture}[node distance = 0mm and 2mm,box/.style={rectangle}]}

我最初尝试构建两个\def,但该尝试在 latexmake 运行中出错。我尝试的想法是用以下内容.style={...}替换中的硬编码值:.style={\Style}

! Package pgfkeys Error: I do not know the key '/tikz/rectangle,draw' and I am
going to ignore it. Perhaps you misspelled it.

经过各种解决方法的尝试后,我改变了对代码的处理方式,如下所示。

尽管工作代码只重复了一行,我还是希望采用某种形式的文本替换解决方案。目前,我缺乏获得线索所需的经验!所有线索都感激不尽。

答案1

这实际上不是一个复杂的解决方案,而仅仅是指出如果您已经加载了 pgf,则无需添加第二个 key val 系统。

\documentclass{article}
\usepackage[margin=0.95in]{geometry}
\usepackage[draft,columns=1]{typogrid}
\usepackage{varwidth}
\usepackage{tikz}
\usetikzlibrary{positioning}

\definecolor{tikzgray}{HTML}{E5E5FF}
\definecolor{tikzyellow}{HTML}{F4F4CC}

\makeatletter
\newif\iftikzbox@draft
\tikzset{tikzbox/.cd,draft/.is if=tikzbox@draft,
    draft/.default=true,
    rule/.initial=0.5in,
    width/.initial=.811\textwidth,
    text/.initial={no text here},
    scolor/.initial=tikzyellow,
    lcolor/.initial=tikzgray}
\newcommand\TikzBox[2][]{\begingroup\tikzset{tikzbox/.cd,#1}%
    \def\commandkey##1{\pgfkeysvalueof{/tikz/tikzbox/##1}}%
    %\rule{\commandkey{rule}}{0pt}%<- what is this good for?
    \iftikzbox@draft
    \begin{tikzpicture}[node distance = 0mm and 2mm,box/.style={rectangle,draw}]
    \else
    \begin{tikzpicture}[node distance = 0mm and 2mm,box/.style={rectangle}]
    \fi 
    \node (n1) [box, fill/.expanded=\commandkey{scolor}] {\commandkey{text}};%
    \node (n2) [box, fill/.expanded=\commandkey{lcolor}, text width=\commandkey{width}, below right=of n1.north east] {%
        \vbox{\noindent\ #2}
    };%
    \end{tikzpicture}\endgroup
}
\makeatother

\def \HELLO {Hello World!}
\def \LTEXT {
Each key (may) store some \emph{tokens} and there exist\newline%
commands, described below, for setting, getting, and\newline%
changing the tokens stored in a key. However, you will\newline%
only very seldom use these commands directly.%
}

\begin{document}

\vspace{\baselineskip}
\TikzBox[text=\HELLO]{\LTEXT}

\TikzBox[draft,text=\HELLO]{\LTEXT}

\end{document}

在此处输入图片描述

正如我所说,这里有很多细节可以改进。这只是为了说明原理。

答案2

我建议expl3使用 key-value 样式。唯一的麻烦是~在 Ti 中使用空格Z 键。避免使用keycommand:它有缺陷且无人维护。

\documentclass{article}
\usepackage[margin=0.95in]{geometry}
\usepackage[draft,columns=1]{typogrid}
\usepackage{varwidth}
\usepackage{xcolor}
\usepackage{tikz}
\usetikzlibrary{positioning}

\definecolor{tikzgray}{HTML}{E5E5FF}
\definecolor{tikzyellow}{HTML}{F4F4CC}

\ExplSyntaxOn

\NewDocumentCommand{\TikzBoxx}{O{}m}
 {
  \keys_set:nn { myers/boxx }
   {
    draft=false,
    rule=.5in,
    width=.811\textwidth,
    text={no~text~here},
    scolor=tikzyellow,
    lcolor=tikzgray,
    #1
   }
  \myers_boxx:n { #2 }
 }

\keys_define:nn { myers/boxx }
 {
  draft  .bool_set:N = \l__myers_boxx_draft_bool,
  draft  .default:n  = true,
  rule   .dim_set:N  = \l__myers_boxx_rule_dim,
  width  .dim_set:N  = \l__myers_boxx_width_dim,
  text   .tl_set:N   = \l__myers_boxx_text_tl,
  scolor .tl_set:N   = \l__myers_boxx_scolor_tl,
  lcolor .tl_set:N   = \l__myers_boxx_lcolor_tl,
 }

\cs_new_protected:Nn \myers_boxx:n
 {
  \bool_if:NTF \l__myers_boxx_draft_bool
   {
    \begin{tikzpicture}[node~distance = 0mm~and~2mm,box/.style={rectangle,draw}]
   }
   {
    \begin{tikzpicture}[node~distance = 0mm~and~2mm,box/.style={rectangle}]
   }
  \rule{\l__myers_boxx_rule_dim}{0pt}
  \node~(n1) [box, fill=\l__myers_boxx_scolor_tl] {\l__myers_boxx_text_tl};
  \node (n2) [
    box,
    fill=\l__myers_boxx_lcolor_tl,
    text~width=\l__myers_boxx_width_dim,
    below~right=of~n1.north~east
  ] { \begin{varwidth}{\l__myers_boxx_width_dim} #1 \end{varwidth} };
  \end{tikzpicture}
 }
\ExplSyntaxOff

\newcommand\HELLO {Hello World!}
\newcommand\LTEXT {%
  Each key (may) store some \emph{tokens} and there exist\newline
  commands, described below, for setting, getting, and\newline
  changing the tokens stored in a key. However, you will\newline
  only very seldom use these commands directly.%
}

\begin{document}

\TikzBoxx[text=\HELLO]{\LTEXT}

\TikzBoxx[text=\HELLO,scolor=red!20,lcolor=green!30,rule=4pt]{\LTEXT}

\TikzBoxx[draft,text=\HELLO,scolor=red!20,lcolor=green!30,rule=4pt]{\LTEXT}

\end{document}

在此处输入图片描述

答案3

编辑:从 1.0 版开始,expkv-cs的行为\ekvcValueSplit略有改变,现在它不再转发键列表和值,而只转发值。第二个示例进行了相应调整。


使用expkv-cs而不是keycommand,您可以创建 key=value 接口,将键的值作为普通参数传入,这样您不必处理\commandkey和朋友的扩展。

expkv-cs由于该机制完全可扩展,因此预定义的可能性是有限的,但我认为对于这个用例,它提供了您所需要的一切。

\documentclass{article}
\usepackage[margin=0.95in]{geometry}
\usepackage[draft,columns=1]{typogrid}
\usepackage{expkv-cs}
\usepackage{tikz}

\definecolor{tikzgray}{HTML}{E5E5FF}
\definecolor{tikzyellow}{HTML}{F4F4CC}

\makeatletter
% \TikzBox@kv will split the key=value list into normal arguments and pass them
% to \TikzBox@do
\ekvcSplitAndForward\TikzBox@kv\TikzBox@do
  {
     style  = {rectangle,draw} % #1
    ,width  = .811\textwidth   % #2
    ,text   = no text here     % #3
    ,scolor = tikzyellow       % #4
    ,lcolor = tikzgray         % #5
    ,pre    = \hfill           % #6
    ,sep    = 2mm              % #7
  }
% We can define other keys that call the existing keys with predefined values.
% The nmeta type will be a key that doesn't take a value (and will throw an
% error if used with a value) and is equivalent to the keys you specify here.
\ekvcSecondaryKeys\TikzBox@kv
  {
     nmeta draft  = {style=rectangle}
    ,nmeta nofill = {pre=}
    ,nmeta blank  = {draft,scolor=white,lcolor=white}
  }
% The user macro will search for the optional argument.
\newcommand\TikzBox[1][]{\TikzBox@kv{#1}}
% \TikzBox@do carries out the actual output. It gets the 7 key-values from
% \TikzBox@kv and grabs an additional argument, which is the mandatory argument
% for \TikzBox from the user's point of view.
\newcommand\TikzBox@do[8]
  {%
    \leavevmode
    #6%
    \begin{tikzpicture}[box/.style={#1}]
      \node (n1) [box, fill=#4] {#3};%
      \node (n2) at (n1.north east)
        [anchor=north west, xshift=#7, box, fill=#5, text width=#2] {\ #8};%
    \end{tikzpicture}%
  }
\makeatother

\def \HELLO {Hello World!}
\def \LTEXT {%
Each key (may) store some \emph{tokens} and there exist\newline%
commands, described below, for setting, getting, and\newline%
changing the tokens stored in a key. However, you will\newline%
only very seldom use these commands directly.%
}

\begin{document}

\TikzBox[text=\HELLO]{\LTEXT}\par
\TikzBox[text=\HELLO,draft]{\LTEXT}\par
\TikzBox[text=\HELLO,blank]{\LTEXT}\par
\TikzBox[width=.5\textwidth,sep=2cm,lcolor=blue]{\LTEXT}\par
\TikzBox[style={circle,draw},width=.55\textwidth,nofill]{\LTEXT}\par

\end{document}

其中一个限制几乎显而易见:输出宏抓取 8 个参数,因此在该实现中只有一个额外键的空间。expkv-cs通过将所有值放在一个参数中并提供从该列表中访问单个值的方法,提供了第二种机制,可以更好地扩展更多键(同时仍可完全扩展)。当您不需要直接访问某个值时,这种方法很有效,但您可以在调用其他宏之前从该列表中拆分单个值。

\documentclass{article}
\usepackage[margin=0.95in]{geometry}
\usepackage[draft,columns=1]{typogrid}
\usepackage{expkv-cs}
\usepackage{tikz}

\definecolor{tikzgray}{HTML}{E5E5FF}
\definecolor{tikzyellow}{HTML}{F4F4CC}

\makeatletter
% \TikzBox@kv will parse the key=value list and put it into one single argument
% in a way that it is easy to access individual values. The style key has to be
% split of beforehand, as we need its value directly so that pgfkeys parses it
% correctly.
\ekvcHash\TikzBox@kv
  {
     style  = {rectangle,draw}
    ,width  = .811\textwidth
    ,text   = no text here
    ,scolor = tikzyellow
    ,lcolor = tikzgray
    ,pre    = \hfill
    ,sep    = 2mm
  }
  {\ekvcValueSplit{style}{#1}{\TikzBox@do{#1}}}
% We can define other keys that call the existing keys with predefined values.
% The nmeta type will be a key that doesn't take a value (and will throw an
% error if used with a value) and is equivalent to the keys you specify here.
\ekvcSecondaryKeys\TikzBox@kv
  {
     nmeta draft  = {style=rectangle}
    ,nmeta nofill = {pre=}
    ,nmeta blank  = {draft,scolor=white,lcolor=white}
  }
% The user macro will search for the optional argument.
\newcommand\TikzBox[1][]{\TikzBox@kv{#1}}
% \TikzBox@do carries out the actual output. It gets the key-values as a list
% from \TikzBox@kv as well as the extracted style key, and grabs an additional
% argument, which is the mandatory argument for \TikzBox from the user's point
% of view.
\newcommand\TikzBox@do[3]
  {%
    \leavevmode
    \ekvcValue{pre}{#1}%
    \begin{tikzpicture}[box/.style={#2}]
      \node (n1) [box, fill=\ekvcValue{scolor}{#1}] {\ekvcValue{text}{#1}};%
      \node (n2) at (n1.north east)
        [
           anchor=north west
          ,xshift=\ekvcValue{sep}{#1}
          ,box
          ,fill=\ekvcValue{lcolor}{#1}
          ,text width=\ekvcValue{width}{#1}
        ]
        {\ #3};%
    \end{tikzpicture}%
  }
\makeatother

\def \HELLO {Hello World!}
\def \LTEXT {%
Each key (may) store some \emph{tokens} and there exist\newline%
commands, described below, for setting, getting, and\newline%
changing the tokens stored in a key. However, you will\newline%
only very seldom use these commands directly.%
}

\begin{document}

\TikzBox[text=\HELLO]{\LTEXT}\par
\TikzBox[text=\HELLO,draft]{\LTEXT}\par
\TikzBox[text=\HELLO,blank]{\LTEXT}\par
\TikzBox[width=.5\textwidth,sep=2cm,lcolor=blue]{\LTEXT}\par
\TikzBox[style={circle,draw},width=.55\textwidth,nofill]{\LTEXT}\par

\end{document}

两个示例都得出相同的结果:

在此处输入图片描述

相关内容