# tcolorbox , tikzmark and distances to the borders of a tcolorbox

The last code below does a good job. Here is the output.

But the API is not efficient. I would like to define my marks as in the following code.

\begin{coding-python}{Basic loop Python}
def oneloop(n):       |\tikzmark{top}|
for i in range(n):
print(i**2)   |\tikzmark{down}|

print(oneloop(n))
\end{coding-python}


Here are my questions.

1. To do that I need to calculate distance to the left margin of the tcolorbox from one mark. Is it possible ?
2. Can I also calculate the distance to the right margin ?

Here is my MWE.

\documentclass[12pt,a4paper]{article}

\usepackage[skins, many, minted, breakable, hooks]{tcolorbox}
\usetikzlibrary{tikzmark,calc,fit}

\makeatletter

% Source: http://tex.stackexchange.com/a/50054/3954

\newcommand\myframe[4][-1ex]{%
\tikz[remember picture,overlay,pin distance=0cm]{
\draw[draw=#4,line width=1pt,fill=#4!20,rectangle,rounded corners, opacity=0.2]
( $(pic cs:#2) + (-1ex,2ex)$ ) rectangle ( $(pic cs:#3) + (1ex,#1)$ );
\draw[draw=#4,line width=1pt,rectangle,rounded corners, opacity=1]
( $(pic cs:#2) + (-1ex,2ex)$ ) rectangle ( $(pic cs:#3) + (1ex,#1)$ );
}
}

% Source: https://tex.stackexchange.com/a/124688/6880

\newcommand{\mynewminted}[3]{%
\newminted[#1]{#2}{#3}%
\tcbset{myminted/#1/.style = {
minted language = #2,
minted options  = {#3}
}}
}

\newcommand{\create@new@coding@env@title}[4]{%
\mynewminted{for-coding-#1}{python}{escapeinside=||}

\newtcblisting[]{coding-#1}[2][1]{%
listing only,
width        = ##1\linewidth,
title        = ##2,
myminted/for-coding-python,
}
}

\create@new@coding@env@title{python}{\Large\faPython}{2mm}{1.8mm}

\makeatother

\begin{document}

\begin{coding-python}{Basic loop Python}
|\tikzmark{topleft}|def oneloop(n):
for i in range(n):
print(i**2)   |\tikzmark{downright}|

print(oneloop(n))
\end{coding-python}

\myframe{topleft}{downright}{red}

\end{document}


Yes, this can be done. One way of accomplishing this is to store the coordinates of the frame of the tcolorbox such that we can use them for the frame. This is achieved by the three keys enhanced,remember,overlay={...}. These coordinates then get used for the horizontal coordinates of the frame.

\documentclass[12pt,a4paper]{article}

\usepackage[skins, many, minted, breakable, hooks]{tcolorbox}
\usetikzlibrary{tikzmark,calc,fit}

\makeatletter

% Source: http://tex.stackexchange.com/a/50054/3954

\newcommand\myframe[4][-1ex]{%
\tikz[remember picture,overlay]{
\path ([yshift=2ex]pic cs:#2) coordinate (aux1)
([yshift=-1ex]pic cs:#3) coordinate (aux2);
\draw[draw=#4,line width=1pt,fill=#4!20,rectangle,rounded corners, opacity=0.2]
([xshift=1ex]current tcb-NW|-aux1)  rectangle
([xshift=-1ex]current tcb-SE|-aux2);
\draw[draw=#4,line width=1pt,rectangle,rounded corners, opacity=1]
([xshift=1ex]current tcb-NW|-aux1)  rectangle
([xshift=-1ex]current tcb-SE|-aux2);
}
}

% Source: https://tex.stackexchange.com/a/124688/6880

\newcommand{\mynewminted}[3]{%
\newminted[#1]{#2}{#3}%
\tcbset{myminted/#1/.style = {
minted language = #2,
minted options  = {#3}
}}
}

\newcommand{\create@new@coding@env@title}[4]{%
\mynewminted{for-coding-#1}{python}{escapeinside=||}

\newtcblisting[]{coding-#1}[2][1]{%
listing only,
width        = ##1\linewidth,
title        = ##2,
\path[overlay,remember picture]
(frame.north west) coordinate (current tcb-NW)
(frame.south east) coordinate (current tcb-SE);}
}
}

\create@new@coding@env@title{python}{\Large\faPython}{2mm}{1.8mm}

\makeatother

\begin{document}

\begin{coding-python}{Basic loop Python}
def oneloop(n):       |\tikzmark{top}|
for i in range(n):
print(i**2)   |\tikzmark{down}|

print(oneloop(n))
\end{coding-python}

\myframe{top}{down}{red}

\end{document}


The distance between the border of the tcolorbox and the red frame is taken to be 1ex but can of course be modified (or stored in a pgf key).

\documentclass[12pt,a4paper]{article}

\usepackage[skins, many, minted, breakable, hooks]{tcolorbox}
\usetikzlibrary{tikzmark}
\tikzset{projetframe/.cd,pstyle/.style={draw=red,line width=1pt,fill=red!20,
rectangle,rounded corners},style/.code={\tikzset{projetframe/pstyle/.append
style={#1}}},frame sep/.initial=1ex,to/.initial={}}%
\newcounter{projetframe}
\newcounter{projetbcframe}
\makeatletter
\newcommand\projetframe[1]{%
\stepcounter{projetframe}%
\tikzmark{pframestart-\number\value{projetframe}}%
\tikzset{projetframe/.cd,#1}%
\tikz[remember picture,overlay,pin distance=0cm]{
\@ifundefined{save@pt@\tikzmark@pp@name{pframestart-\number\value{projetframe}}}{%
%
}{%
\path ([yshift=2ex]pic cs:pframestart-\number\value{projetframe}) coordinate (aux1)
([yshift=-1ex]pic cs:\pgfkeysvalueof{/tikz/projetframe/to}) coordinate (aux2)
(pic cs:current tcb-NW-\number\value{projetbcframe}) coordinate(aux3)
(pic cs:current tcb-SE-\number\value{projetbcframe}) coordinate (aux4);
\draw[projetframe/pstyle]
([xshift=\pgfkeysvalueof{/tikz/projetframe/frame sep}]aux3|-aux1)  rectangle
([xshift=-\pgfkeysvalueof{/tikz/projetframe/frame sep}]aux4|-aux2);
}
}%
}

% Source: http://tex.stackexchange.com/a/50054/3954

% Source: https://tex.stackexchange.com/a/124688/6880

\newcommand{\mynewminted}[3]{%
\newminted[#1]{#2}{#3}%
\tcbset{myminted/#1/.style = {
minted language = #2,
minted options  = {#3}
}}
}

\newcommand{\create@new@coding@env@title}[4]{%
\mynewminted{for-coding-#1}{python}{escapeinside=||}

\newtcblisting[]{coding-#1}[2][1]{%
listing only,
width        = ##1\linewidth,
title        = ##2,
\path[overlay,remember picture]
(frame.north west) node{\tikzmark{current tcb-NW-\number\value{projetbcframe}}}
(frame.south east) node{\tikzmark{current tcb-SE-\number\value{projetbcframe}}};}
\stepcounter{projetbcframe}%
}
}

\create@new@coding@env@title{python}{\Large\faPython}{2mm}{1.8mm}

\makeatother

\begin{document}

\begin{coding-python}{Basic loop Python}
|\projetframe{to=down}|def oneloop(n):
for i in range(n):
print(i**2)   |\tikzmark{down}|

print(oneloop(n))
\end{coding-python}

\tikzset{projetframe/.cd,style={draw=blue,fill=blue!20},frame sep=1.5ex}
\begin{coding-python}{Another basic loop Python}
|\projetframe{to=downB}|def oneloop(n):
for i in range(n):
print(i**2)    |\tikzmark{downB}|

print(oneloop(n))
\end{coding-python}

\end{document}


As you can see, everything is now stored in pgf keys, so you can create the boxes more easily. However, one could think of making this even more elegant by passing all the options to the argument, which would allow you to get rid of the \tikzset. My main problem when trying to make this more elegant is that I do not know much about minted. There seem to be some forbidden characters, and I dunno what these are nor how to get around this.

• Thanks for this answer. That works so well and the comment helps to understand the trick. – projetmbc Oct 13 at 20:51
• Thanks a lot ! The second API for the well placed frames is good but it will be better to write |\projetframe{to=downB}| on the right so as to not break the reading of the listing. Is there a technical impossibility to do that ? – projetmbc Oct 14 at 7:02
• @projetmbc I am skeptical that there is a reasonably simple way of doing this because then the ordering will be wrong. That is, you can move it to the right but then the code left of it will be covered by the background of the frame. It may be possible to cook something up by "hacking" the layers of tcolorbox but this will be a very different approach. – Schrödinger's cat Oct 14 at 7:10
• Thanks for this clarification. – projetmbc Oct 14 at 7:12
• @projetmbc One thing that may be helpful is to recall that tikzmark does have means to annotate listings without putting anything into the code of the listings. This is possible with the tikzmark library listings. AFAIK this only works with codes typeset using the listings package. Unfortunately I am a complete noob with regards to all these listings, so I do not know to which extent this can be applied here. – Schrödinger's cat Oct 14 at 7:26