lvjr / tabularray

Typeset tabulars and arrays with LaTeX3
https://ctan.org/pkg/tabularray
247 stars 22 forks source link

Cannot use `\expanded` in `\SetTblrInner` and/or as a part of key values #435

Open Aljumaily opened 10 months ago

Aljumaily commented 10 months ago

I want to dynamically choose some of the key values of my table. For instance, \mycolour will be red and I want to have the table to use that value. To the best of my knowledge, I am able to use \expanded{...} to expand a control sequence. This works inside the table content but not inside \SetTblrInner nor as a value of a key.

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\ExplSyntaxOn
\tl_new:N \mybody
\tl_new:N \mycolour
\tl_set:Nn \mybody {a & b & c\\ d & e & f\\ }
\tl_set:Nn \mycolour {red}
\ExplSyntaxOff

\NewTblrEnviron{mytable}
\SetTblrOuter[mytable]{tall}
\SetTblrInner[mytable]{
  % vlines={\expanded{\mycolor}}, % Doesn't work!!
}

\begin{document}
\begin{mytable}[expand=\expanded]{
    % vlines={\expanded{\mycolor}} % Doesn't work!!
  }
  \expanded{\mybody}
  \expanded{\mycolour}
\end{mytable}
\end{document}

Which gives:

Undefined control sequence. \l__tblr_v_tl ->\expanded {\mycolor 
muzimuzhi commented 10 months ago

First, expand=\cmd can only expand \cmd in a table body, not the inner and outer specifications. Then \mycolour is defined but you tried to use \mycolor, US spelling c-o-l-o-r, which is surely undefined.

vlines={fg=\mycolour} does the trick.

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\ExplSyntaxOn
\tl_new:N \mybody
\tl_new:N \mycolour
\tl_set:Nn \mybody {a & b & c\\ d & e & f\\ }
\tl_set:Nn \mycolour {red}
\ExplSyntaxOff

\NewTblrEnviron{mytable}
\SetTblrOuter[mytable]{tall}
\SetTblrInner[mytable]{
  vlines={fg=\mycolour}
}

\begin{document}
\begin{mytable}[expand=\expanded]{
     vlines={fg=\mycolour}
  }
  \expanded{\mybody}
\end{mytable}
\end{document}

image

BTW in some cases (when the replacement text of \mybody is not fully expandable) you may need \expandeonce from etoolbox package, which is defined as

\newcommand{\expandonce}[1]{%
  \unexpanded\expandafter{#1}}
Aljumaily commented 10 months ago

@muzimuzhi Thank you for the reply! Even if \mycolour was used consistently, an error would have been encountered. The fix, as you suggested, is to use the key fg then assign it the value. I want to go a step further. Suppose I want to store all of the key-value pairs in a variable called \mystyle and then pass that variable into \SetTblrInner and/or as a part of the style in the table. Even with \expandafter, I get the error Package tabularray: Unexpandable command \expandafter inside column (tabularray) type!. Please note that I am quite new to expl3 and don't have sufficient knowledge with the idea of expansion.

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\ExplSyntaxOn
\tl_new:N \mybody
\tl_new:N \mystyle
\tl_set:Nn \mybody {a & b & c\\ d & e & f\\ }
\tl_set:Nn \mystyle {vlines={fg=red}}
\ExplSyntaxOff

\NewTblrEnviron{mytable}
\SetTblrOuter[mytable]{tall}

\SetTblrInner[mytable]{\mystyle}% No error but doesn't work

\begin{document}
\begin{mytable}[expand=\expanded]{
  \expandafter\mystyle % Error!
}
  \expanded{\mybody}
\end{mytable}
\end{document}
muzimuzhi commented 10 months ago

Expansion control is a topic lives as long as TeX, even before birth of expl3. With primitive \expanded introduced in middle 2019, more expansion control can be done expandably.

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\ExplSyntaxOn
\tl_new:N \mybody
\tl_new:N \mystyle
\tl_set:Nn \mybody {a & b & c\\ d & e & f\\ }
\tl_set:Nn \mystyle {vlines={fg=red}}
\ExplSyntaxOff

\NewTblrEnviron{mytable}
\SetTblrOuter[mytable]{tall}

% less readable
\expanded{\unexpanded{\SetTblrInner[mytable]}\unexpanded\expandafter{\expandafter{\mystyle}}}

% more readable
\NewDocumentCommand{\SetTblrInnerFromCmd}{ O{tblr} m }{%
  \expanded{%
    \unexpanded{\SetTblrInner[#1]}%
    \unexpanded\expandafter{\expandafter{#2}}%
  }%
}

\def\anotherstyle{hlines={fg=-red,wd=1pt}}
\SetTblrInnerFromCmd[mytable]{\anotherstyle}

% make the implementation more readable, with the help of argument processor
% \TakeValue, see texdoc usrguide, sec. 2.10 _Argument processors_
\NewDocumentCommand{\SetTblrInnerFromCmdX}{ O{tblr} >{\TakeValue} m }{%
  \SetTblrInner[#1]{#2}%
}
\ExplSyntaxOn
\cs_new_protected:Npn \TakeValue #1
  {
    % or \tl_set:NV, which works for more expl3 data types
    \tl_set:No \ProcessedArgument {#1}
  }
\ExplSyntaxOff

\def\yetanotherstyle{row{1}={bg=orange!20}}
\SetTblrInnerFromCmdX[mytable]{\yetanotherstyle}

\begin{document}
\begin{mytable}[expand=\expanded]{
%  \expandafter\mystyle % Error!
}
  \expanded{\mybody}
\end{mytable}
\end{document}

image

Aljumaily commented 10 months ago

Thank you for the multiple examples and clever solutions. I have been reading interface3 documentation but not usrguide, which I have started to do so. I have modified your suggestion and used the most comfortable version:

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\ExplSyntaxOn
\tl_new:N \mybody
\tl_new:N \mystyleA
\tl_new:N \mystyleB
\tl_new:N \mystyleC
\tl_set:Nn \mybody {a & b & c\\ d & e & f\\ }
\tl_set:Nn \mystyleA {vlines={fg=red, wd=0.5pt}}
\tl_set:Nn \mystyleB {row{1}={bg=lightgray}, column{1}={c, 4em}}
\tl_set:Nn \mystyleC {hlines={fg=blue, wd=1pt}}

\NewDocumentCommand{\SetTblrInnerFromCmdX}{O{tblr} >{\TakeValue} m}{
  \SetTblrInner[#1]{#2}
}

\cs_new_protected:Npn \TakeValue #1 {
  \tl_set:Ne \ProcessedArgument {#1}
}
\ExplSyntaxOff

\NewTblrEnviron{mytable}
\SetTblrOuter[mytable]{tall}
\SetTblrInnerFromCmdX[mytable]{\mystyleA, \mystyleB, \mystyleC}

\begin{document}
\begin{mytable}[expand=\expanded]{}
    \expanded{\mybody}
\end{mytable}
\end{document}

While it satisfies my requirement, I still don't understand the internal definition of \ProcessedArgument{...}. So, I want to have \SetTblrInnerFromCmdX to take care of the objective accepting {O{tblr} m} as the parameters without the usage of external functions. Here is what I have come up with and would appreciate any thoughts that you might have (in terms of restrictions, simplicity and/or practicality):

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\ExplSyntaxOn
\tl_new:N \mybody
\tl_new:N \myTempVar % Can use \l_tmpa_tl but would rather use my own scratch variables
\tl_new:N \mystyleA
\tl_new:N \mystyleB
\tl_new:N \mystyleC
\tl_set:Nn \mybody {a & b & c\\ d & e & f\\ }
\tl_set:Nn \mystyleA {vlines={fg=red, wd=0.5pt}}
\tl_set:Nn \mystyleB {row{1}={bg=lightgray}, column{1}={c, 4em}}
\tl_set:Nn \mystyleC {hlines={fg=blue, wd=1pt}}

\NewDocumentCommand{\SetTblrInnerFromCmdX}{O{tblr} m}{
  \tl_set:Nx \myTempVar {#2}
  \clist_map_inline:Nn {\myTempVar}{\SetTblrInner[#1]{##1}}
}
\ExplSyntaxOff

\NewTblrEnviron{mytable}
\SetTblrOuter[mytable]{tall}

\begin{document}
\SetTblrInnerFromCmdX[mytable]{\mystyleA, \mystyleB, \mystyleC}

\begin{mytable}[expand=\expanded]{}
    \expanded{\mybody}
\end{mytable}
\end{document}
image