0

Using the method explained in https://tex.stackexchange.com/a/213413/218142 I can click on the code in a listings environment defined with lstnewenvironment. Students cut and paste their code from GlowScript.org into the newglowscriptblock environment, provide a caption and a URL so that when I click on the code I'm taken to their program so I can run it. I could probably leave things as they are, but is there a way to encapsulate this into a suitably defined environment? In other words, is there a way to include the use of the box within the environment definition or to define a new environment that make this all just work as simply as cutting and pasting the code into the environment? (I have a style defined with lstdefinestyle but I didn't include it here.) I would also like to be able to click anywhere on the figure and not just on the actual code. Can that be accommodated too?

Here's my MWE and its output.

\documentclass{article}
\usepackage{listings}
\lstnewenvironment{newglowscriptblock}[3]{%
  \lstset{caption={#1},label={#2}}}{}
\usepackage{hyperref}
\hypersetup{pdfborder=0 0 0}
\begin{document}

% See https://tex.stackexchange.com/a/213413/218142 \newsavebox\lstA \begin{lrbox}{\lstA} \begin{newglowscriptblock}{Problem 37 Code}{pref2}{} GlowScript 3.0 VPython

a Young's modulus problem

Lo = 3 # wire's original length in m d = 3e-3 # wire's diameter in m g = 9.8 # surface grav. field strength in N/kg m = 10 # ball's mass in kg Y = 2e11 # steel's Young's modulus in N/m^2

Find DeltaL, the amount the wire stretches.

area = (pid2)/4 Force = mg DeltaL = (ForceLo)/(areaY)

print ("The wire stretches by ",DeltaL," m") \end{newglowscriptblock} \end{lrbox} % The URL here is would the #3 in the definition of newglowscriptenvironment \href{https://google.com}{\usebox{\lstA}}

\end{document}

Output code listing

  • 1
    One trick is to overlap what you see on top of a boxed hyperlink, See https://tex.stackexchange.com/questions/176280/image-link-clickable-in-non-white-areas-excluding-the-background/261071?r=SearchResults&s=2|20.3985#261071 for example. – John Kormylo Oct 03 '20 at 19:29

2 Answers2

1

This might do the trick - but - no warranties! ;-)

\documentclass{article}

\usepackage{listings} \usepackage[colorlinks=true]{hyperref} %\usepackage[colorlinks=false]{hyperref}

\makeatletter \newsavebox\newglowscriptblockTempBox \newcommand\newglowscriptblockURL{}% \newcommand\newglowscriptblockWrapInPartopsep[1]{% \vskip\partopsep#1\vskip\partopsep }% \lstnewenvironment{newglowscriptblock}[3]{% \gdef\newglowscriptblockURL{#3}% \setbox\newglowscriptblockTempBox\hbox{\begingroup\aftergroup}% \lstset{caption={#1},label={#2}}% }{% \endgroup \hypersetup{hidelinks}% \ifvmode\endgraf\expandafter\newglowscriptblockWrapInPartopsep\else\endgraf\expandafter@firstofone\fi {% \vskip\topsep \noindent \expandafter\href\expandafter{\newglowscriptblockURL}{\usebox{\newglowscriptblockTempBox}}% \endgraf \vskip\topsep }% }% % Make sure hyperref's \href gets its argument "normalized @ifdefinable\newglowscriptblock@@{% \let\newglowscriptblock@@=\newglowscriptblock@ \renewcommand\newglowscriptblock@[2]{\hyper@normalise{\newglowscriptblock@@\unexpanded{{#1}{#2}}}}% }% \makeatother

\begin{document}

\lstlistoflistings

\newpage

Some text. Some text. Some text. Some text. \begin{newglowscriptblock}{Problem 37 Code}{pref2}{https://google.com} GlowScript 3.0 VPython

a Young's modulus problem

Lo = 3 # wire's original length in m d = 3e-3 # wire's diameter in m g = 9.8 # surface grav. field strength in N/kg m = 10 # ball's mass in kg Y = 2e11 # steel's Young's modulus in N/m^2

Find DeltaL, the amount the wire stretches.

area = (pid2)/4 Force = mg DeltaL = (ForceLo)/(areaY)

print ("The wire stretches by ",DeltaL," m") \end{newglowscriptblock} Some text. Some text. Some text. Some text.

\newpage

nameref-link to listing: \nameref{pref2}

ref-link to listing: \ref{pref2}

pageref-link to listing: \pageref{pref2}

autoref-link to listing: \autoref{pref2}

\end{document}


Addendum in October 21, 2020

In a comment from October 20, 2020 you asked:

If I don't want to supply a URL, is there a way to suppress hyperref's warning about an empty target OR can I supply something that won't make the code highlight if I click it with the mouse?

The pdf-viewer used by me doesn't highlight things when clicking with the mouse. I assume the highlighting is not due to the pdf-file but is a feature "hardwired" in your pdf-viewing program.

If you don't mind changing the order of the arguments of the environment so that the argument holding the URL will not be the third one but will be the first one, you can make that argument optional, with an empty default.

You need to do some trickery to have the URL-argument normalized for hyperref. Otherwise you cannot have characters like # within the URL.

Then you can add forking to check whether the expansion of the macro \newglowscriptblockURL defined from the URL-argument yields "blankness", i.e., emptiness or explicit space tokens only, and apply \href only if this is not the case. This way if the now optional URL-argument is not provided or is provided empty, attempts at creating a hyperlink will not take place:

\documentclass{article}

\usepackage{listings} \usepackage[colorlinks=true]{hyperref} %\usepackage[colorlinks=false]{hyperref}

\makeatletter

% Infrastructure for "hyperref-normalizing" an optional argument of a \lstnewenvironment: \begingroup \catcode\^^A=14 % \catcode^^M\active^^A \catcode`%\active^^A @ifdefinable\hyper@@normaliseoptarg{^^A \gdef\hyper@@normaliseoptarg#1#2#3]{^^A \edef\Hy@tempa{^^A \endgroup\noexpand#1[{\Hy@RemovePercentCr#3%^^M@nil}]^^A }^^A \Hy@tempa^^A }^^A }^^A \endgroup \newcommand\MY@exchange[2]{#2#1}% \expandafter\newcommand\expandafter*\expandafter\hyper@normaliseoptarg\expandafter{% \romannumeral0% \expandafter\MY@exchange\expandafter{\hyper@normalise}{% @firstofone{\expandafter} % \MY@exchange{\let\hyper@@normalise=\hyper@@normaliseoptarg}% }% }% \newcommand\lstenv@testhyper@normalizeopt[2]{% @ifnextchar[{\catcode \active 5\relax\hyper@normaliseoptarg{\lstenv@testopt\noexpand#1{#2}}}% {\hyper@normaliseoptarg{#1}[{#2}]}% }% % end of Infrastructure for "hyperref-normalizing" an optional argument of a \lstnewenvironment.

\newsavebox\newglowscriptblockTempBox \newcommand\newglowscriptblockURL{}% \newcommand\newglowscriptblockWrapInPartopsep[1]{% \vskip\partopsep#1\vskip\partopsep }% \lstnewenvironment{newglowscriptblock}[3][]{% \gdef\newglowscriptblockURL{#1}% \setbox\newglowscriptblockTempBox\hbox{\begingroup\aftergroup}% \lstset{caption={#2},label={#3}}% }{% \endgroup \ifx\newglowscriptblockURL\empty\else\hypersetup{hidelinks}\fi \ifvmode\endgraf\expandafter\newglowscriptblockWrapInPartopsep\else\endgraf\expandafter@firstofone\fi {% \vskip\topsep \noindent \ifcat $\detokenize\expandafter\expandafter\expandafter{\expandafter@firstoftwo\newglowscriptblockURL{}.}$% \expandafter@firstoftwo\else\expandafter@secondoftwo\fi {@firstofone}% {\expandafter\href\expandafter{\newglowscriptblockURL}}% {\usebox{\newglowscriptblockTempBox}}% \endgraf \vskip\topsep }% }% % Make sure hyperref's \href gets its argument "hyperref-normalized" by replacing % \lstenv@testopt with \lstenv@testhyper@normalizeopt % in the definition of \newglowscriptblock@. \expandafter\renewcommand\expandafter*\expandafter\newglowscriptblock@\expandafter{% \romannumeral0% \expandafter\MY@exchange\expandafter{% \csname\string\newglowscriptblock@\endcsname{}%<-the default for the opt arg is between these braces. The default is empty. }{ % \ifx\protect@typeset@protect \expandafter\lstenv@testhyper@normalizeopt \else @x@protect\newglowscriptblock@ \fi }% }% \makeatother

\begin{document}

\lstlistoflistings

\newpage

Some text. Some text. Some text. Some text. The optional argument is not empty, thus the environment is a hyperlink. % \begin{newglowscriptblock}[https://google.com#Weird]{Problem 37 Code}{pref2} GlowScript 3.0 VPython

a Young's modulus problem

Lo = 3 # wire's original length in m d = 3e-3 # wire's diameter in m g = 9.8 # surface grav. field strength in N/kg m = 10 # ball's mass in kg Y = 2e11 # steel's Young's modulus in N/m^2

Find DeltaL, the amount the wire stretches.

area = (pid2)/4 Force = mg DeltaL = (ForceLo)/(areaY)

print ("The wire stretches by ",DeltaL," m") \end{newglowscriptblock} The optional argument is not empty, thus the environment is a hyperlink. Some text. Some text. Some text. Some text.

\newpage

Some text. Some text. Some text. Some text. The optional argument is empty, thus the environment is not a hyperlink. \begin{newglowscriptblock}{Problem 37 Code}{pref3} GlowScript 3.0 VPython

a Young's modulus problem

Lo = 3 # wire's original length in m d = 3e-3 # wire's diameter in m g = 9.8 # surface grav. field strength in N/kg m = 10 # ball's mass in kg Y = 2e11 # steel's Young's modulus in N/m^2

Find DeltaL, the amount the wire stretches.

area = (pid2)/4 Force = mg DeltaL = (ForceLo)/(areaY)

print ("The wire stretches by ",DeltaL," m") \end{newglowscriptblock} The optional argument is empty, thus the environment is not a hyperlink. Some text. Some text. Some text. Some text.

\newpage

nameref-link to listing pref2: \nameref{pref2}

ref-link to listing pref2: \ref{pref2}

pageref-link to listing pref2: \pageref{pref2}

autoref-link to listing pref2: \autoref{pref2}

nameref-link to listing pref3: \nameref{pref3}

ref-link to listing pref3: \ref{pref3}

pageref-link to listing pref3: \pageref{pref3}

autoref-link to listing pref3: \autoref{pref3}

\end{document}

Ulrich Diez
  • 28,770
  • Yes indeed this seems to work prefectly. Thank you so much! – LaTeXereXeTaL Oct 03 '20 at 02:23
  • Okay I'll change it in my document. – LaTeXereXeTaL Oct 03 '20 at 02:25
  • 1
    @LaTeXereXeTaL Probably hidelinks is to prefer to pdfborder=0 0 0. In case of having colorlinks setting the pdfborder will have no effect but hidelinks will ensure that the listing is in the usual text-color, not in the color of links. – Ulrich Diez Oct 03 '20 at 02:48
  • Got it. If I put some text before and after the newglowscript environment there is no spacing between the environment and the text. I've tried inserting \par but that doesn't work. – LaTeXereXeTaL Oct 03 '20 at 03:06
  • 2
    @LaTeXereXeTaL You can place the box holding the listing between some \vskip. I editied to use partopsep and topsep as enumerate/itemize and the like do. – Ulrich Diez Oct 03 '20 at 03:23
  • Works perfectly. Thank you again! – LaTeXereXeTaL Oct 03 '20 at 03:28
  • 2
    @LaTeXereXeTaL Now I've inserted some \endgraf for ensuring vertical mode for the vskips and some \noindent for ensuring horizontal mode without horizontal paragraph-indenting for the box holding the listing. – Ulrich Diez Oct 03 '20 at 03:41
  • This turned into an interesting and educational question after all. – LaTeXereXeTaL Oct 03 '20 at 03:47
  • 1
    @LaTeXereXeTaL Yes. We can make it really hard by asking for the phrase "GlowScript 3.0 VPython" to be prepended as the first line of the listing by the environment itself automatically. ;-) (I think this would require a totally different approach, probably an environment which reads its body in verbatim-catcode-régime, prepends the line and then passes everything, wrapped into some lrbox- and listing-environment to \scantokens...) ;-) – Ulrich Diez Oct 03 '20 at 03:51
  • If I don't want to supply a URL, is there a way to suppress hyperref's warning about an empty target OR can I supply something that won't make the code highlight if I click it with the mouse? – LaTeXereXeTaL Oct 19 '20 at 22:19
  • Thank you! I experimented with an optional argument but for some reason couldn't get it to work. I will study your solution. – LaTeXereXeTaL Oct 20 '20 at 15:55
  • 1
    Acknowledged, and thank you! – LaTeXereXeTaL Oct 21 '20 at 18:32
  • For some reason, # is converted to %23 and I get a 404 error with %23 in the URL but not with #. Any idea how to correct that? The link I'm testing is https://www.glowscript.org/#/user/heafnerj/folder/mandidemo/program/mandidemo . – LaTeXereXeTaL Jan 04 '21 at 03:24
  • Seems to be a known bug in Apple PDF viewers and browsers. – LaTeXereXeTaL Jan 04 '21 at 06:27
1

For reference, I include this answer. I discovered that tcolorbox can do this quite simply in one command. The entire interior of the box is clickable (it doesn't work of course in the screenshot below but it will work in your document) and the URL can be set as an optional parameter to the new command.

% !TEX program = lualatexmk
% !TEX encoding = UTF-8 Unicode

\documentclass{article} \usepackage[left=1.00in,right=1.00in]{geometry} \usepackage[most]{tcolorbox} % loads the listings library \usepackage{hyperref} \hypersetup{colorlinks=true}

\definecolor{gsbggray} {rgb}{0.90,0.90,0.90} % background gray \definecolor{gsgray} {rgb}{0.30,0.30,0.30} % gray \definecolor{gsgreen} {rgb}{0.00,0.60,0.00} % green \definecolor{gsorange} {rgb}{0.80,0.45,0.12} % orange \definecolor{gspeach} {rgb}{1.00,0.90,0.71} % peach \definecolor{gspearl} {rgb}{0.94,0.92,0.84} % pearl \definecolor{gsplum} {rgb}{0.74,0.46,0.70} % plum \lstdefinestyle{vpython}{% % style for listings backgroundcolor=\color{gsbggray},% % background color basicstyle=\footnotesize,% % default style breakatwhitespace=true% % break at whitespace breaklines=true,% % break long lines captionpos=b,% % position caption classoffset=1,% % STILL DON'T UNDERSTAND THIS commentstyle=\color{gsgray},% % font for comments deletekeywords={print},% % delete keywords from the given language emph={self,cls,@classmethod,@property},% % words to emphasize emphstyle=\color{gsorange}\itshape,% % font for emphasis escapeinside={(@}{@)},% % add LaTeX within your code frame=tb,% % frame style framerule=2.0pt,% % frame thickness framexleftmargin=5pt,% % extra frame left margin %identifierstyle=\sffamily,% % style for identifiers keywordstyle=\sffamily\color{gsplum},% % color for keywords language=Python,% % select language linewidth=\linewidth,% % width of listings morekeywords={% % VPython/GlowScript specific keywords future,abs,acos,align,ambient,angle,append,append_to_caption,% append_to_title,arange,arrow,asin,astuple,atan,atan2,attach_arrow,% attach_trail,autoscale,axis,background,billboard,bind,black,blue,border,% bounding_box,box,bumpaxis,bumpmap,bumpmaps,camera,canvas,caption,capture,% ceil,center,clear,clear_trail,click,clone,CoffeeScript,coils,color,combin,% comp,compound,cone,convex,cos,cross,curve,cyan,cylinder,data,degrees,del,% delete,depth,descender,diff_angle,digits,division,dot,draw_complete,% ellipsoid,emissive,end_face_color,equals,explog,extrusion,faces,factorial,% False,floor,follow,font,format,forward,fov,frame,gcurve,gdisplay,gdots,% get_library,get_selected,ghbars,global,GlowScript,graph,graphs,green,gvbars,% hat,headlength,headwidth,height,helix,hsv_to_rgb,index,interval,keydown,% keyup,label,length,lights,line,linecolor,linewidth,logx,logy,lower_left,% lower_right,mag,mag2,magenta,make_trail,marker_color,markers,material,% max,min,mouse,mousedown,mousemove,mouseup,newball,norm,normal,objects,% offset,one,opacity,orange,origin,path,pause,pi,pixel_to_world,pixels,plot,% points,pos,pow,pps,print,print_function,print_options,proj,purple,pyramid,% quad,radians,radius,random,rate,ray,read_local_file,readonly,red,redraw,% retain,rgb_to_hsv,ring,rotate,round,scene,scroll,shaftwidth,shape,shapes,% shininess,show_end_face,show_start_face,sign,sin,size,size_units,sleep,% smooth,space,sphere,sqrt,start,start_face_color,stop,tan,text,textpos,% texture,textures,thickness,title,trail_color,trail_object,trail_radius,% trail_type,triangle,trigger,True,twist,unbind,up,upper_left,upper_right,% userpan,userspin,userzoom,vec,vector,vertex,vertical_spacing,visible,% visual,vpython,VPython,waitfor,white,width,world,xtitle,yellow,yoffset,% ytitle% },% morekeywords={print,None,TypeError},% % additional keywords morestring=[b]{"""},% % treat triple quotes as strings numbers=left,% % where to put line numbers numbersep=10pt,% % how far line numbers are from code numberstyle=\bfseries\tiny,% % set to 'none' for no line numbers showstringspaces=false,% % show spaces in strings showtabs=false,% % show tabs within strings stringstyle=\color{gsgreen},% % color for strings upquote=true,% % how to typeset quotes }%

\NewTCBListing[auto counter,list inside=gsprogs]{tcbglowscriptblock}{ O{} D(){glowscript.org} m }{% breakable,% center,% code = \newpage,% %derivpeach,% enhanced,% hyperurl interior = https://#2,% label = {gs:\thetcbcounter},% left = 8mm,% list entry = \texttt{GlowScript} Program \thetcbcounter: #3,% listing only,% listing style = vpython,% nameref = #3,% title = \texttt{GlowScript} Program \thetcbcounter: #3,% width = 0.9\textwidth,% #1, }%

\begin{document}

\begin{tcbglowscriptblock}(google.com){A short \texttt{GlowScript} Program} GlowScript 3.0 vpython

scene.width = 400 scene.height = 760

constants and data

g = 9.8 # m/s^2 mball = 0.03 # kg Lo = 0.26 # m ks = 1.8 # N/m deltat = 0.01 # s

objects (origin is at ceiling)

ceiling = box(pos=vector(0,0,0), length=0.2, height=0.01, width=0.2) ball = sphere(pos=vector(0,-0.3,0),radius=0.025, color=color.orange) spring = helix(pos=ceiling.pos, axis=ball.pos-ceiling.pos, color=color.cyan,thickness=0.003,coils=40, radius=0.010)

initial values

pball = mball * vector(0,0,0) # kg m/s Fgrav = mball * g * vector(0,-1,0) # N t = 0

improve the display

scene.autoscale = False # turn off automatic camera zoom scene.center = vector(0,-Lo,0) # move camera down scene.waitfor('click') # wait for a mouse click

initial calculation loop

calculation loop

while t < 10: rate(100) # we need the stretch s = mag(ball.pos) - Lo # we need the spring force Fspring = ks * s * -norm(spring.axis) Fnet = Fgrav + Fspring pball = pball + Fnet * deltat ball.pos = ball.pos + (pball / mball) * deltat spring.axis = ball.pos - ceiling.pos t = t + deltat \end{tcbglowscriptblock}

The program \ref{gs:1} is nice. It's called \nameref{gs:1} on page \pageref{gs:1}.

\end{document}

MWE Output