2

I'm making a document where I want the definitions of entries in the glossary to appear as tooltips.

The tooltip code itself is from the answer: Can't embed hyperlink references in a tooltip

My function takes 3 arguments, the first being the name for defining function for the tooltip, the second being the name to appear in the glossary - which may be more than 1 word, and the 3rd to be the definition.

\newcommand{\defTerm}[3]{%
\expandafter\newcommand\csname #1\endcsname[1]{%
{\textbf{\tooltip{##1}{\begin{varwidth}{10cm}#3\end{varwidth}}}}%
}%
\newglossaryentry{#1}{name=#2,description={#3}}%
}

Ideally the tooltips with the defintions would also work for terms in the glossary itself. This would mean that in some cases in the body text, the 3rd argument would contain a command for creating a tooltip.

For example:

\defTerm{GP}{GP}{The ground plane.}
\defTerm{Transverse}{Transverse}{This is orthogonal to the \GP{GP}.}

Then in the text

The \Transverse{transverse} plane is ...

Which fails as it tries to create a tooltip with the contents

This is orthogonal to the \GP{GP}.

however \GP{GP} tries to make a tooltip within this.

What I'd like to achieve is to modify my \defTerm command so that when the tooltip command is used, regex replaces any commands within the definition (argument #3) with \textbf. In normal regex searching for: (\\[A-Z]\w+{)([a-zA-Z\s]*)(}) and replacing with \textbf{$2}

Here I have 2 issues. I have the regex somewhat working in a test environment however I can't find how to search for the backslash character. Possibly this has something to do with the compiling order. My second issue is to do with how/were to use \ExplSyntaxOn and \ExplSyntaxOff. My working non-working command is

% Command to add items to glossary and create the command for using tooltips
\newcommand{\defTerm}[3]{%
\expandafter\newcommand\csname #1\endcsname[1]{%
    \ExplSyntaxOn
    \tl_set:Nn \l_tmpa_tl { #3 }
    \regex_replace_all:nnN {([A-Z]\w+{)([a-zA-Z\s]*)(})} {textbf{\2}} \l_tmpa_tl %Also should have backslash
    \tl_use:N \l_tmpa_tl
    \ExplSyntaxOff
    {\textbf{\tooltip{##1}{\begin{varwidth}{10cm}#3\end{varwidth}}}}%
}%
\newglossaryentry{#1}{name=#2,description={#3}}%
}

Below is complete working code. I have the changes I'd like to make commented out.

\documentclass{article}
\usepackage{varwidth} % To allow maximum width boxes in tooltips
\usepackage{tooltips}

\begin{document}
\defTerm{GP}{GP}{The ground plane.}
\defTerm{Transverse}{Transverse}{This is orthogonal to the GP.}
%\defTerm{Transverse}{Transverse}{This is orthogonal to the \GP{GP}.} % What I would like to use instead of the line above
The \Transverse{transverse} plane is ...
\end{document} 

And tooltips.sty is below. To be honest I don't understand much within the \tootip command. I only need to option with no asterisks, and don't need to change the font colour so any simplification to this would also be appreciated.

% https://tex.stackexchange.com/questions/397639/cant-embed-hyperlink-references-in-a-tooltip

\ProvidesPackage{tooltips}

% Command to add items to glossary and create the command for using tooltips
\newcommand{\defTerm}[3]{%
\expandafter\newcommand\csname #1\endcsname[1]{%
    % \ExplSyntaxOn
    % \tl_set:Nn \l_tmpa_tl { #3 }
    % \regex_replace_all:nnN {([A-Z]\w+{)([a-zA-Z\s]*)(})} {textbf{\2}} \l_tmpa_tl %Also should have backslash
    % \tl_use:N \l_tmpa_tl
    % \ExplSyntaxOff
    {\textbf{\tooltip{##1}{\begin{varwidth}{10cm}#3\end{varwidth}}}}%
}%
%\newglossaryentry{#1}{name=#2,description={#3}}%
}


\usepackage{pdfbase}[2017/03/16]
\usepackage{xparse,ocgbase}
\usepackage{xcolor,calc}
\usepackage{tikzpagenodes,linegoal}
\usetikzlibrary{calc}

\ExplSyntaxOn
\let\tpPdfLink\pbs_pdflink:nn
\let\tpPdfAnnot\pbs_pdfannot:nnnn\let\tpPdfLastAnn\pbs_pdflastann:
\let\tpAppendToFields\pbs_appendtofields:n
\def\tpPdfXform{\pbs_pdfxform:nnnnn{1}{1}{}{}}
\let\tpPdfLastXform\pbs_pdflastxform:
\let\cListSet\clist_set:Nn\let\cListItem\clist_item:Nn
\ExplSyntaxOff

\makeatletter
\NewDocumentCommand{\tooltip}{%
   ssssO{\ifdefined\@linkcolor\@linkcolor\else black\fi}mO{yellow!20}mO{0pt,0pt}%
}{{%
  \leavevmode%
  \IfBooleanT{#2}{%
    %for variants with two and more stars, put tip box on a PDF Layer (OCG)
\ocgbase@new@ocg{tipOCG.\thetcnt}{%
      /Print<</PrintState/OFF>>/Export<</ExportState/OFF>>%
    }{false}%
    \xdef\tpTipOcg{\ocgbase@last@ocg}%
    %prevent simultaneous visibility of multiple non-draggable tooltips
    \ocgbase@add@ocg@to@radiobtn@grp{tool@tips}{\ocgbase@last@ocg}%
  }%
  \tpPdfLink{%
    \IfBooleanTF{#4}{%
      /Subtype/Link/Border[0 0 0]/A <</S/SetOCGState/State [/Toggle \tpTipOcg]>>
    }{%
      /Subtype/Screen%
      /AA<<%
        \IfBooleanTF{#3}{%
          /E<</S/SetOCGState/State [/Toggle \tpTipOcg]>>%
        }{%
          \IfBooleanTF{#2}{%
            /E<</S/SetOCGState/State [/ON \tpTipOcg]>>%
            /X<</S/SetOCGState/State [/OFF \tpTipOcg]>>%
           }{
            \IfBooleanTF{#1}{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                }%
                if(fd.display==display.hidden){%
                  fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }else{%
                  if(!click\thetcnt&&!dragging\thetcnt)
 {fd.display=display.hidden;}%
                  if(!dragging\thetcnt){click\thetcnt=false;}%
                }%
                this.dirty=false;%
              )>>%
            }{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                 }%
                if(fd.display==display.hidden){%
                   fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }%
              this.dirty=false;%
              )>>%
              /X<</S/JavaScript/JS(%
                if(!click\thetcnt&&!dragging\thetcnt){fd.display=display.hidden;}%
                if(!dragging\thetcnt){click\thetcnt=false;}%
                this.dirty=false;%
              )>>%
            }%
            /U<</S/JavaScript/JS(click\thetcnt=true;this.dirty=false;)>>%
            /PC<</S/JavaScript/JS (%
              var fd=this.getField('tip.\thetcnt');%
              try{fd.rect=fdor\thetcnt;}catch(e){}%
              fd.display=display.hidden;this.dirty=false;%
             )>>%
            /PO<</S/JavaScript/JS(this.dirty=false;)>>%
          }%
        }%
      >>%
    }%
  }{{\color{#5}#6}}%
  \sbox\tiptext{%
    \IfBooleanT{#2}{%
       \ocgbase@oc@bdc{\tpTipOcg}\ocgbase@open@stack@push{\tpTipOcg}}%
    \fcolorbox{black}{#7}{#8}%
    \IfBooleanT{#2}{\ocgbase@oc@emc\ocgbase@open@stack@pop\tpNull}%
  }%
   \cListSet\tpOffsets{#9}%
   \edef\twd{\the\wd\tiptext}%
   \edef\tht{\the\ht\tiptext}%
   \edef\tdp{\the\dp\tiptext}%
   \tipshift=0pt%
   \IfBooleanTF{#2}{%
     %OCG-based (that is, all non-draggable) boxes should not extend beyond the
     %current column as they may get overlaid by text in the neighbouring column
     \setlength\whatsleft{\linegoal}%
   }{%
     \measureremainder{\whatsleft}%
   }%
   \ifdim\whatsleft<\dimexpr\twd+\cListItem\tpOffsets{1}\relax%
     \setlength\tipshift{\whatsleft-\twd-\cListItem\tpOffsets{1}}\fi%
   \IfBooleanF{#2}{\tpPdfXform{\tiptext}}%
   \raisebox{\heightof{#6}+\tdp+\cListItem\tpOffsets{2}}[0pt][0pt]{%
     \makebox[0pt][l]{\hspace{\dimexpr\tipshift+\cListItem\tpOffsets{1}\relax}%
     \IfBooleanTF{#2}{\usebox{\tiptext}}{%
       \tpPdfAnnot{\twd}{\tht}{\tdp}{%
         /Subtype/Widget/FT/Btn/T (tip.\thetcnt)%
         /AP<</N \tpPdfLastXform>>%
         /MK<</TP 1/I \tpPdfLastXform/IF<</S/A/FB true/A [0.0 0.0]>>>>%
         /Ff 65536/F 3%
         /AA <<%
           /U <<%
             /S/JavaScript/JS(%
               var fd=event.target;%
               var mX=this.mouseX;var mY=this.mouseY;%
               var drag=function(){%
                 var nX=this.mouseX;var nY=this.mouseY;%
                 var dX=nX-mX;var dY=nY-mY;%
                 var fdr=fd.rect;%
                 fdr[0]+=dX;fdr[1]+=dY;fdr[2]+=dX;fdr[3]+=dY;%
                 fd.rect=fdr;mX=nX;mY=nY;%
               };%
               if(!dragging\thetcnt){%
                 dragging\thetcnt=true;Int=app.setInterval("drag()",1);%
               }%
               else{app.clearInterval(Int);dragging\thetcnt=false;}%
               this.dirty=false;%
             )%
           >>%
         >>%
       }%
       \tpAppendToFields{\tpPdfLastAnn}%
     }%
   }}%
   \stepcounter{tcnt}%
 }}
 \makeatother
 \newsavebox\tiptext\newcounter{tcnt}
 \newlength{\whatsleft}\newlength{\tipshift}
 \newcommand{\measureremainder}[1]{%
   \begin{tikzpicture}[overlay,remember picture]
     \path let \p0 = (0,0), \p1 = (current page.east) in
       [/utils/exec={\pgfmathsetlength#1{\x1-\x0}\global#1=#1}];
   \end{tikzpicture}%
 }
3

I would recommend the following:

\newcommand\tooltip@textbf[9]{\textbf{#6}}
\DeclareExpandableDocumentCommand{\tooltip}{%
   ssssO{\ifdefined\@linkcolor\@linkcolor\else black\fi}mO{yellow!20}O{0pt,0pt}m%
}{{%
  \expandafter\let\csname tooltip code\endcsname\tooltip@textbf
   % ... rest of macro definition here
   %
}

This makes \tooltip define itself to be \textbf. Note the use of \DeclareExpandableDocumentCommand -- commands used inside the argument text of \tooltip must be expandable (this is the error that's occurring with nested tooltips since the \tooltip command is not itself expandable). Also note that I swapped the order of arguments 8 and 9 of \tooltip. This is because expandable commands cannot end in an optional argument. This avoids the need for any fancy regex.

For your \defTerm command, I recommend that you add a special prefix to the commands that you define and a special "accessor" command. This allows you to have a term whose name is the same as a common command, and makes it easier to see which macros are terms. This change has nothing to do with the solution to the tooltip problem.

\newcommand\useTerm[1]{\csname term-#1\endcsname}
\newcommand{\defTerm}[3]{%
    \expandafter\newcommand\csname term-#1\endcsname[1]{%
    {\tooltip{\textbf{##1}}{\begin{varwidth}{10cm}#3\end{varwidth}}}%
    }%
    \newglossaryentry{#1}{name=#2,description={#3}}%
}

Finally, if you were going to use an expl3 solution, note that \makeatletter, \makeatletter, \ExplSyntaxOn, and \ExplSyntaxOff all normally should happen outside of the command. So for example:

% Wrong, leads to "Undefined control sequence \tl"
\newcommand\mycommand{%
    \ExplSyntaxOn
    \tl_set:Nn \l_tmpa_tl { something }
    % ... more stuff
    \ExplSyntaxOff
}

% Right (or at least it will work)
\ExplSyntaxOn
\newcommand\mycommand{
    \tl_set:Nn \l_tmpa_tl { something }
    % ... more stuff
}
\ExplSyntaxOff

% Right:
\makeatletter
\newcommand\mycommand{\@firstofone}
\makeatother

Okay, so here's the full code. Note that I've removed the glossaries package and so I commented out the \newglossaryentry line. That's because the glossaries package has nothing to do with any part of your question. If you use this, you can put back in \usepackage{glossaries} and uncomment \newglossaryentry.

\documentclass{article}
\usepackage{varwidth} % To allow maximum width boxes in tooltips
\usepackage{tooltips}
\begin{document}
\defTerm{GP}{GP}{The ground plane.}
\defTerm{Transverse}{Transverse}{This is orthogonal to the \useTerm{GP}{GP}.} % What I would like to use instead of the line above

The \useTerm{Transverse}{transverse} plane is ...
\end{document} 

And tooltips.sty:

\ProvidesPackage{tooltips}

% Command to add items to glossary and create the command for using tooltips

\newcommand\useTerm[1]{\csname term-#1\endcsname}
\newcommand{\defTerm}[3]{%
    \expandafter\newcommand\csname term-#1\endcsname[1]{%
    {\tooltip{\textbf{##1}}{\begin{varwidth}{10cm}#3\end{varwidth}}}%
    }%
    %\newglossaryentry{#1}{name=#2,description={#3}}%
}




\usepackage{pdfbase}[2017/03/16]
\usepackage{xparse,ocgbase}
\usepackage{xcolor,calc}
\usepackage{tikzpagenodes,linegoal}
\usetikzlibrary{calc}

\ExplSyntaxOn
\let\tpPdfLink\pbs_pdflink:nn
\let\tpPdfAnnot\pbs_pdfannot:nnnn\let\tpPdfLastAnn\pbs_pdflastann:
\let\tpAppendToFields\pbs_appendtofields:n
\def\tpPdfXform{\pbs_pdfxform:nnnnn{1}{1}{}{}}
\let\tpPdfLastXform\pbs_pdflastxform:
\let\cListSet\clist_set:Nn\let\cListItem\clist_item:Nn
\ExplSyntaxOff

\makeatletter
\newcommand\tooltip@textbf[9]{{#6}}
\DeclareExpandableDocumentCommand{\tooltip}{%
   ssssO{\ifdefined\@linkcolor\@linkcolor\else black\fi}mO{yellow!20}O{0pt,0pt}m%
}{{%
  \expandafter\let\csname tooltip code\endcsname\tooltip@textbf
  \leavevmode%
  \IfBooleanT{#2}{%
    %for variants with two and more stars, put tip box on a PDF Layer (OCG)
\ocgbase@new@ocg{tipOCG.\thetcnt}{%
      /Print<</PrintState/OFF>>/Export<</ExportState/OFF>>%
    }{false}%
    \xdef\tpTipOcg{\ocgbase@last@ocg}%
    %prevent simultaneous visibility of multiple non-draggable tooltips
    \ocgbase@add@ocg@to@radiobtn@grp{tool@tips}{\ocgbase@last@ocg}%
  }%
  \tpPdfLink{%
    \IfBooleanTF{#4}{%
      /Subtype/Link/Border[0 0 0]/A <</S/SetOCGState/State [/Toggle \tpTipOcg]>>
    }{%
      /Subtype/Screen%
      /AA<<%
        \IfBooleanTF{#3}{%
          /E<</S/SetOCGState/State [/Toggle \tpTipOcg]>>%
        }{%
          \IfBooleanTF{#2}{%
            /E<</S/SetOCGState/State [/ON \tpTipOcg]>>%
            /X<</S/SetOCGState/State [/OFF \tpTipOcg]>>%
           }{
            \IfBooleanTF{#1}{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                }%
                if(fd.display==display.hidden){%
                  fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }else{%
                  if(!click\thetcnt&&!dragging\thetcnt)
 {fd.display=display.hidden;}%
                  if(!dragging\thetcnt){click\thetcnt=false;}%
                }%
                this.dirty=false;%
              )>>%
            }{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                 }%
                if(fd.display==display.hidden){%
                   fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }%
              this.dirty=false;%
              )>>%
              /X<</S/JavaScript/JS(%
                if(!click\thetcnt&&!dragging\thetcnt){fd.display=display.hidden;}%
                if(!dragging\thetcnt){click\thetcnt=false;}%
                this.dirty=false;%
              )>>%
            }%
            /U<</S/JavaScript/JS(click\thetcnt=true;this.dirty=false;)>>%
            /PC<</S/JavaScript/JS (%
              var fd=this.getField('tip.\thetcnt');%
              try{fd.rect=fdor\thetcnt;}catch(e){}%
              fd.display=display.hidden;this.dirty=false;%
             )>>%
            /PO<</S/JavaScript/JS(this.dirty=false;)>>%
          }%
        }%
      >>%
    }%
  }{{\color{#5}#6}}%
  \sbox\tiptext{%
    \IfBooleanT{#2}{%
       \ocgbase@oc@bdc{\tpTipOcg}\ocgbase@open@stack@push{\tpTipOcg}}%
    \fcolorbox{black}{#7}{#9}%
    \IfBooleanT{#2}{\ocgbase@oc@emc\ocgbase@open@stack@pop\tpNull}%
  }%
   \cListSet\tpOffsets{#8}%
   \edef\twd{\the\wd\tiptext}%
   \edef\tht{\the\ht\tiptext}%
   \edef\tdp{\the\dp\tiptext}%
   \tipshift=0pt%
   \IfBooleanTF{#2}{%
     %OCG-based (that is, all non-draggable) boxes should not extend beyond the
     %current column as they may get overlaid by text in the neighbouring column
     \setlength\whatsleft{\linegoal}%
   }{%
     \measureremainder{\whatsleft}%
   }%
   \ifdim\whatsleft<\dimexpr\twd+\cListItem\tpOffsets{1}\relax%
     \setlength\tipshift{\whatsleft-\twd-\cListItem\tpOffsets{1}}\fi%
   \IfBooleanF{#2}{\tpPdfXform{\tiptext}}%
   \raisebox{\heightof{#6}+\tdp+\cListItem\tpOffsets{2}}[0pt][0pt]{%
     \makebox[0pt][l]{\hspace{\dimexpr\tipshift+\cListItem\tpOffsets{1}\relax}%
     \IfBooleanTF{#2}{\usebox{\tiptext}}{%
       \tpPdfAnnot{\twd}{\tht}{\tdp}{%
         /Subtype/Widget/FT/Btn/T (tip.\thetcnt)%
         /AP<</N \tpPdfLastXform>>%
         /MK<</TP 1/I \tpPdfLastXform/IF<</S/A/FB true/A [0.0 0.0]>>>>%
         /Ff 65536/F 3%
         /AA <<%
           /U <<%
             /S/JavaScript/JS(%
               var fd=event.target;%
               var mX=this.mouseX;var mY=this.mouseY;%
               var drag=function(){%
                 var nX=this.mouseX;var nY=this.mouseY;%
                 var dX=nX-mX;var dY=nY-mY;%
                 var fdr=fd.rect;%
                 fdr[0]+=dX;fdr[1]+=dY;fdr[2]+=dX;fdr[3]+=dY;%
                 fd.rect=fdr;mX=nX;mY=nY;%
               };%
               if(!dragging\thetcnt){%
                 dragging\thetcnt=true;Int=app.setInterval("drag()",1);%
               }%
               else{app.clearInterval(Int);dragging\thetcnt=false;}%
               this.dirty=false;%
             )%
           >>%
         >>%
       }%
       \tpAppendToFields{\tpPdfLastAnn}%
     }%
   }}%
   \stepcounter{tcnt}%
 }}
 \makeatother
 \newsavebox\tiptext\newcounter{tcnt}
 \newlength{\whatsleft}\newlength{\tipshift}
 \newcommand{\measureremainder}[1]{%
   \begin{tikzpicture}[overlay,remember picture]
     \path let \p0 = (0,0), \p1 = (current page.east) in
       [/utils/exec={\pgfmathsetlength#1{\x1-\x0}\global#1=#1}];
   \end{tikzpicture}%
 }
  • I've implemented your solution and turned to glossary back on which is working as expected, however within the tooltip popup I only want the other defined terms bold, so in this case the "GP" but not the remainder of the text. – Elise Feb 27 '18 at 21:56
  • Updated my answer to fix that issue. – Hood Chatham Feb 27 '18 at 23:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.