I'm trying to tightly wrap some expressions in containers (could be Panel
, Pane
, maybe Framed
in some cases -- it varies on the use-case) the way a normal expression cell's frame are laid-out.
That is, if you select a simple expression like x
, you get the following:
Whereas, even with PaneMargins
and ImageMargins
set to 0
, selecting e.g. a Pane
results in this:
One way to calculate the borders/margins between the inner expression and the bounding box of the expression when placed inside a container such as Pane
is using ImageBorders
. That part is straightforward enough, and not the subject of this question (although I'd welcome more efficient methods than rasterizing and then using ImageBorders
! EDIT: Found an alternative, see the end of this post.).
The subject of this question is: Can someone provide a general-case description, or link to documentation, for the alignment behavior of Pane
, Panel
, Framed
, and any other bonus containers, with respect to options such as Alignment
and FrameMargins
? Particularly, when ImageSize
is less than what would be returned by ImageSize@Rasterize@expr
and FrameMargins
contains negative values, I find that the resulting output between the different containers varies quite a bit.
EDIT: As suggested by Carl Woll in a comment below, setting ContentPadding->False
ought to solve this question. However, I used that in my example below, and it didn't work. I believe that wrapping e.g. a Pane
in e.g. a Grid
causes the Pane
's ContentPadding
option to not be respected. Maybe that behavior is the crux of this post.
It's easier to post a GIF animation exported from Mathematica than to explain much further. On first inspection, it looks like there are two classes of containers: Pane
and Row
on the one hand, and Framed
and Panel
on the other. But if you look closely, as glyphs "scroll into the inside of each container", the behavior is different between e.g. Framed
and Panel
when the margins are negative. It makes getting a general-case solution tough when you're trying to tightly wrap expressions, but don't want them clipped.
Animation below:
And here is a version with the other margin dimension being animated:
REFERENCE:
The code used to generate the above plots is shown below:
MakeContainerSwatch[expr_, height_, alignment_] :=
Module[{rulesCommon, rulesPadding, rulesFrame, rulesColor,
rulesColorPane, align, margins, pane1, pane2, pane3, framed1,
framed2, framed3, panel1, panel2, panel3, row1, row2, row3},
rulesCommon = Sequence[
ImageSize -> {Automatic, height},
ImageMargins -> {{0, 0}, {0, 0}},
BaseStyle -> "Output"
];
rulesPadding = Sequence[ContentPadding -> False];
rulesFrame = Sequence[Frame -> True];
rulesColor = Sequence[Background -> LightBlue];
rulesColorPane =
Sequence[DefaultBaseStyle -> {Background -> LightBlue}];
align[y_] = Alignment -> {Center, y};
margins[align_] = FrameMargins -> {{0, 0}, align};
pane1 =
Pane[expr, rulesCommon, rulesPadding, rulesColorPane, align[-1],
margins[alignment]];
pane2 =
Pane[expr, rulesCommon, rulesPadding, rulesColorPane, align[0],
margins[alignment]];
pane3 =
Pane[expr, rulesCommon, rulesPadding, rulesColorPane, align[1],
margins[alignment]];
framed1 =
Framed[expr, rulesCommon, rulesPadding, rulesColor, align[-1],
margins[alignment]];
framed2 =
Framed[expr, rulesCommon, rulesPadding, rulesColor, align[0],
margins[alignment]];
framed3 =
Framed[expr, rulesCommon, rulesPadding, rulesColor, align[1],
margins[alignment]];
panel1 =
Panel[expr, rulesCommon, rulesPadding, rulesColor, align[-1],
margins[alignment]];
panel2 =
Panel[expr, rulesCommon, rulesPadding, rulesColor, align[0],
margins[alignment]];
panel3 =
Panel[expr, rulesCommon, rulesPadding, rulesColor, align[1],
margins[alignment]];
row1 = Row[{expr}, rulesCommon, rulesFrame, rulesColor, align[-1],
margins[alignment]];
row2 = Row[{expr}, rulesCommon, rulesFrame, rulesColor, align[0],
margins[alignment]];
row3 = Row[{expr}, rulesCommon, rulesFrame, rulesColor, align[1],
margins[alignment]];
{"pn", pane1, pane2, pane3, "fr", framed1, framed2, framed3, "pl",
panel1, panel2, panel3, "rw", row1, row2, row3}
]
sampleExpression = "a\nb\nc\nd\ne\nf\ng\nh";
dummyPane1 = Pane["*", BaseStyle -> "Output"];
dummyPane2 = Pane["*\n*\n*\n*\n*\n*\n*\n*\n", BaseStyle -> "Output"];
containerSwatch := With[
{
bottomLabel = StringTemplate["Bottom: `bot`"][<|"bot" -> bot|>],
topLabel = StringTemplate["Top: `top`"][<|"top" -> top|>]
},
Grid[List /@ {
Row@{
Panel[
ParametricPlot[{bot, y}, {y, 0, 10},
PlotRange -> {{-40, 40}, {0, 10}}, PlotLabel -> bottomLabel,
Axes -> {True, False}, ImageSize -> Medium],
ImageSize -> Scaled[0.49], Alignment -> Center],
Panel[
ParametricPlot[{top, y}, {y, 0, 10},
PlotRange -> {{-40, 40}, {0, 10}}, PlotLabel -> topLabel,
Axes -> {True, False}, ImageSize -> Medium],
ImageSize -> Scaled[0.49], Alignment -> Center]
},
Grid@List@{
StyleHeading["Clipped: "],
Splice@
Prepend[dummyPane2]@
MakeContainerSwatch[sampleExpression,
154 - 80, {bot, top}],
StyleHeading[", Oversized: "],
Splice@
Prepend[dummyPane2]@
MakeContainerSwatch[sampleExpression, 154 + 80, {bot, top}]
}
},
ItemSize -> Full
]
]
containerSwatchAnimation1 =
Table[containerSwatch, {top, -40, 40, 20}, {bot, -40, 40, 2}];
containerSwatchAnimation2 =
Table[containerSwatch, {bot, -40, 40, 20}, {top, -40, 40, 2}];
Export["containerSwatchAnimation1.gif",
Flatten@containerSwatchAnimation1, ImageResolution -> 72/2]
Export["containerSwatchAnimation2.gif",
Flatten@containerSwatchAnimation2, ImageResolution -> 72/2]
EDIT: For posterity, here is a fast way to get the size you'd need to use to make a tightly-wrapped container. Credit goes to this post by b3m2a1: https://mathematica.stackexchange.com/a/166772/76328
FrontEndExecute@
GetBoundingBoxSizePacket@
Cell[
BoxData["aaaaaaa\na\na\na\na" // ToBoxes],
"Output",
PageWidth -> Infinity,
ShowCellBracket -> False,
CellMargins -> {{0, 0}, {0, 0}}
]
It's not the same as using ImageRaster
and then the combination of ImageDimensions
and BorderDimensions
, but it's so much faster, and if I get the ContentPadding
option (noted by Carl Woll below) working with Grid
s, it'll be sufficient for my task-at-hand (a ragged Grid
of Row
s of Pane
s of alternating colors, where the Row
s are expected to line-wrap, but you want the Pane
s to be the same height for aesthetics).
Pane[x,ContentPadding->False]
$\endgroup$Pane
withContentPadding->False
is what I'm looking for, except for one catch: I usedContentPadding->False
in the above example, and it was ignored. I wouldn't have dug further if you hadn't posted, so thanks. It looks like theContentPadding
option does what I want, EXCEPT if you then nest thePane
in e.g. aGrid
, in which case it gets ignored. Any ideas on how to work around that? Nesting the containers is a common use-case for me. I may switch from usingGrid
to manually laying-out everything withRow
s. $\endgroup$ContentPadding
at the time I tried it (it turns out that mySpacings
specification was poor, and I did not specifyItemSizes->{0,0}
:Grid[List@List@Framed[".", ImageMargins -> {{0, 0}, {0, 0}}, FrameMargins -> {{0, 0},{0, 0}}, ContentPadding -> False], Frame -> All, Spacings -> 0]
$\endgroup$