# How exactly is the argument supplied if an explicit function is given as LegendLayout?

The documentation of LineLegend, PointLegend etc. says:

The layout specification f is applied to the table of values {{col1, lbl1}, ...} to generate the legend.

This seems to be confirmed by the following:

With[{
colors = {Red, Blue},
labels = {x, y},
layout = # &},
LineLegend[colors, labels, LegendLayout -> layout]
]


This example with Lists as labels also works as expected:

With[{
colors = {Red, Blue},
labels = {{a, b}, {x, y}},
layout = # &},
LineLegend[colors, labels, LegendLayout -> layout]
]


And so does this one with a more complicated layout function:

With[{
colors = {Red, Blue},
labels = {{a, b}, {x, y}},
layout = #[[All, 2]] &},
LineLegend[colors, labels, LegendLayout -> layout]
]


However, the following example breaks:

With[{
colors = {Red, Blue},
labels = {{a, b}, {x, y}},
layout = #[[All, 2, 1]] &},
LineLegend[colors, labels, LegendLayout -> layout]
]


I expected {a, x} as the result. What is going wrong? What does the legend function do to the labels such that their parts cannot be extracted?

(All following observations were made in Mathematica 10.1 -- details may be version specific.)

A peek at the InputForm is informative. I'll shorten Graphics expressions for clarity.

With[{colors = {Red, Blue}, labels = {{a, b}, {x, y}},
layout = Print[InputForm@# /. Graphics[___] :> "Graphics[<<>>]"] &},
LineLegend[colors, labels, LegendLayout -> layout]]

{{"Graphics[<<>>]", RawBoxes[#1]}, {"Graphics[<<>>]", RawBoxes[#2]}}


So it seems that labels are not actually filled in at the time the LegendLayout function is evaluated; rather it is used to create another function that eventually receives the values.

A look at part of the Box structure that is produced when the legend is displayed shows in more detail what happens.

legend = With[{colors = {Red, Blue}, labels = {{a, b}, {x, y}}},
LineLegend[colors, labels, LegendLayout -> foo]];

dfn = DisplayFunction /. Options[ToBoxes@legend, DisplayFunction];

dfn /. _GraphicsBox :> "GraphicsBox[<<>>]"

StyleBox[StyleBox[
PaneBox[RowBox[{"foo", "[",
RowBox[{"{",
RowBox[{RowBox[{"{", RowBox[{"GraphicsBox[<<>>]", ",", #1}], "}"}], ",",
RowBox[{"{", RowBox[{"GraphicsBox[<<>>]", ",", #2}], "}"}]}], "}"}],
"]"}], Alignment -> Left, AppearanceElements -> None,
ImageMargins -> {{5, 5}, {5, 5}}, ImageSizeAction -> "ResizeToFit"],
LineIndent -> 0, StripOnInput -> False], {FontFamily -> "Arial"},
Background -> Automatic, StripOnInput -> False] &


I used foo as a dummy function just to show where it ends up in this, and I abbreviated the GraphicsBox expressions to reduce verbosity.

This ends up being the real function used to produce the legend, as we see if we give it some dummy values:

dfn["one", "two"] // RawBoxes


By the time these are filled in foo has been turned into an inert structure, so it cannot change these values.

• It generates TemplateBox and those Slots are filled from template's first argument. This way labels structure will never be accessible for LegendLayout function. Documentation is not precise, to say at least.
– Kuba
Mar 9, 2017 at 12:32
• @Kuba Yes, I was just tracking that down myself and figuring out how to write that is an informative way. Mar 9, 2017 at 12:33

This is less of an answer but more like a reason why it was designed in that way.

You may have noticed that all the legend functions(Swatch, Point, Line) except BarLegend allow interactive editing: you can toggle to the label by mouse or Tab key and then change it. Because of this design decision and also the implementation, label is atomic and thus LegendLayout has the limitation of looking into what labels are. With the current implementation, it seems logically impossible to achieve both.

Mr. Wizard's impressive observation provides the evidence of the implementation which is using TemplateBox.

• Thank you! I am always interested in the reason behind the design. Mar 9, 2017 at 23:36