pgf-tikz / pgf

A Portable Graphic Format for TeX
https://pgf-tikz.github.io/
1.08k stars 104 forks source link

Add an import mechanism #1284

Open tobiasBora opened 9 months ago

tobiasBora commented 9 months ago

Brief outline of the proposed feature

I often find myself creating nested styles like:

\pgfkeys{
  /mystyle/.style={
    set fill/.style={fill=##1, opacity=.5},
  }
}

While it works nicely, it can quickly become a mess: for instance, to edit later set fill, I need to do something like /mystyle/.append style={set fill/.style={new def}}, and it can quickly be hard to read and not super robust. Moreover, this makes it hard to to call a single style (e.g. how do I load the style set fill without loading all styles in mystyle?) I’m also even more worried by efficiency: in practice, I call mystyle in every single node that I am drawing and it can be quite time consuming.

I know it is possible to do .search also={/conf my style}, but then I need to start every single style with /tikz/.cd, and, more importantly, this solution assumes that I run this into tikz, but in practice it might not be the case. For instance, I might call it inside a tikzcd matrix that starts with somtething like /tikzcd/commutative diagram/.cd: so if I do /tikz/.cd I am unable to see stuff defined in /tikzcd/commutative diagram… and I also get some breakages that I cannot explain.

It would be much more handy to have a .import={/conf mystyle} that takes all keys defined in /conf mystyle and just "insert" them here, basically like if I did a copy paste of all keys in /conf mystyle/.

Usage example

Try to uncomment the line /some other libraries/.cd,, and you will see the issue. On the other hand, \myNodeInneficient has this line uncommented and it works… but I need to copy/paste the whole definition and/or use nested styles, which I don't like.

\documentclass[options]{article}

\usepackage{tikz}
\usepackage{etoolbox}

\begin{document}

\makeatletter
\pgfkeys{
  /test/.cd,
  every node/.style={},
  nodes/.code={%
    \scantokens{%
      \pgfkeysalso{%
        /test/every node/.append style={#1}%
      }%
    }%
  },%
  /conf test/.cd,
  %% Here I put many helper functions
  my opacity/.style={
    /tikz/.cd,%%% <--- I don't want this: what if I call it outside of tikz?
    opacity=#1,
  },
  set semi fill/.style={
    /tikz/.cd,%%% <--- I don't want this: what if I call it outside of tikz?
    set fill=#1,
    my opacity=.5
  },
}

\pgfkeys{
  /test/nodes={
    set fill/.style={fill=#1},
    set fill=green,
  },
}

\pgfkeys{
  /some other libraries/.cd,
  .search also={/tikz},
}

\NewDocumentCommand{\myNode}{O{}m}{
  \node[
    %% Try to uncomment this: it will all break, while this should technically also search in /tikz
    %/some other libraries/.cd,
    draw, .search also={/conf test}, /test/every node,
  #1]{#2};
}

%% This is like \myNode, but I copy/pasted the keys in /conf test instead of using .search also
\NewDocumentCommand{\myNodeInneficient}{O{}m}{
  \node[
    %% See: here I can uncomment this line
    /some other libraries/.cd,
    draw,
    %% Here I put many helper functions
    my opacity/.style={
      opacity=#1,
    },
    set semi fill/.style={
      set fill=#1,
      my opacity=.5
    },
    /test/every node,
  #1]{#2};
}

\makeatother

\begin{tikzpicture}
  \myNode[
  %set semi fill=red
  ]{Hey}
  \myNodeInneficient[xshift=2cm]{Not efficient}
\end{tikzpicture}

\end{document}
hmenke commented 9 months ago

.search also is not additive. It overrides any previously present .unknown handler. You need to explicitly list all the namespaces you want to have searched.

@@ -45,8 +45,8 @@
 \NewDocumentCommand{\myNode}{O{}m}{
   \node[
     %% Try to uncomment this: it will all break, while this should technically also search in /tikz
-    %/some other libraries/.cd,
-    draw, .search also={/conf test}, /test/every node,
+    /some other libraries/.cd,
+    draw, .search also={/conf test,/tikz}, /test/every node,
   #1]{#2};
 }
tobiasBora commented 9 months ago

Thanks, but wouldn’t it be possible to create an additive version of .search also? (actually, if it is not additive, shouldn’t it be named .search instead?) It might be hard (or impossible if the user also import some styles?) to track all paths that have been previously configured.

Ideally, this version could also allow the import individual styles, like:

\pgfkeys{
  /my styles/.cd,
  style A/.style={fill=red},
  style B/.style={fill=green},
  style C/.style={fill=red},

  /group style/.cd,
  style D/.style={fill=red},
  style E/.style={fill=green},
  style F/.style={fill=red},
}
% …
\node[
  .import={/group style, /my styles/style A, /my styles/style B},
  % here I should be able to call "style {A,B,D,E,F}", but not "style C"
  style A,
]{};
muzimuzhi commented 9 months ago

A <key>/.import={<full path list>} handler calls for keeping track of all known keys in a tree structure. Only then it's possible to implement something like

# expressed as python import statements
# https://docs.python.org/3/reference/simple_stmts.html#the-import-statement
from group_style import *
from my_styles import style_A, style_B

An additive <key>/.search also needs to determine if the corresponding <full path>/.unknown/.@cmd is already set by previous use(s) of .search also.

Maybe both can start with third-party pgfkeys libraries.

hmenke commented 9 months ago

Isn't this what pgfkeysfiltered is supposed to do?

tobiasBora commented 9 months ago

@hmenke pgfkeysfiltered seems to solve another question I was having, but I don't see how it helps here (at least to solve the initial problem, it might help to do partial import, but since we do not have complete input yet it is maybe a bit early): the doc says that it does exactly like \pgfkeys except that it might decide not to evaluate some keys… but the problem is that the /.cd already removes some keys, so we want to add keys, not remove even more keys. Am I missing something?