How to properly display a Showybox with a calculated width?

I’m trying to define a function that returns a showybox for which the width is either a user-given width or when not specified, the width of the body or 95% of the width of the parent item, whatever is smaller.

This is my attempt

#let defbox(self: none, title: none, width: none, body) = context {
  layout(size => [
    #let w = 0.0
    #if width == none { w = calc.min(size.width,measure(body).width) } else { w = width }
    #showybox(
      width: w,
      title-style: (
        boxed-style: (
          anchor: (x: left, y: horizon),
        ),
        weight: "bold",
      ),
      title: title,
      body + [#w] + [ #size.width] + [ #measure(body).width],
    )
  ]
)
}

As you can see, I added some “debugging” to the body because I have some issues.

What I observe is that

  1. If the body width is larger than the page width, it’s ok.
#defbox(
  title: "Cross section",
  "A kind of probability that a particle will undergo a certain interaction with a specific nucleus.",
)


2. If the body width is smaller than the page width, it seems to cut off the body even more but I don’t understand on what the cut off is based. Example below: plenty of white space left

#defbox(
  title: [Fission yield [informal]],
  [The relative amount produced of a fission product per nuclide fissioned],
)


3. If I have math and itemized lists, it completely miscalculates the body width.

#{
  show math.equation: set text(size: 0.8em)

  grid(
    columns: (1fr, 1fr),
    inset: (x: 10pt),
    defbox(title: "Maxwell spectrum", [
      #v(1em)
      - $χ(E) dd(E) = (2π)/(π T)^(#sfrac(3, 2)) sqrt(E) exp(-E/T)dd(E)$
      - $dash(E)_text("fiss") = 3/2 T$
      - $hat(E)_text("fiss") = 1/2 T$

      #v(2em)

      _For #ce("^235U")_
      - $T = #qty("1.33", "mega electronvolt")$
      - $dash(E)_text("fiss") ≈ #qty("2", "mega electronvolt"), hat(E)_text("fiss") ≈ #qty("0.67", "mega electronvolt")$
    ]),
    defbox(title: "Cranberg spectrum", [
      #v(1em)
      - $χ(E) dd(E) = (2 exp(- (A B)/4)) / sqrt(π A^3 B) exp(- E/A) sinh(sqrt(B E)) dd(E)$
      - $dash(E)_text("fiss") = (A^2 B +6A)/4$
      - $hat(E)_text("fiss") = …$

      #v(2em)

      _For #ce("^235U")_
      - $A = #num("0.965")$, $B = #qty("2.29", "per mega electronvolt")$
      - $dash(E)_text("fiss") ≈ #qty("1.98", "mega electronvolt"), hat(E)_text("fiss") ≈ #qty("0.72", "mega electronvolt")$
    ]),
  )
}


4. Having images gives widths of zero…

#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(title: "Maxwell spectrum", align(horizon + center)[
    #image("/plots/PromptChiMaxwell.svg", fit: "contain", width: 95%)
  ]),
  defbox(title: "Cranberg spectrum", align(horizon + center)[
    #image("/plots/PromptChiCranberg.svg", fit: "contain", width: 95%)
  ]),
)

So what I’m doing wrong here? What am I missing?

Hi, can you post complete examples? I cannot reproduce the problem with lists and math: after “fixing” the code to make it compile it seems to work fine.

For the extra space on the right of the text, in the box: that’s because the paragraph is not justified. If you do set par(justify: true) it will be tight.

For the problem that the box is made too small to fit the text, even when there should be enough space: I think that’s because #showybox(width: ...) makes the box itself wide enough for the text, but then the text cannot take this full width, it has to go inside of the box, with “margins”, so it doesn’t fit on one line.

Hey,

I think I came up with a MWE to demonstrate different things. For some context: I’m using Touying to develop course slides.

The list stuff I mentioned in my first post is provoked by using the itemize package, not by math.

Thanks for any clarification on what mistakes I made (really trying to learn here, not just copy/paste working code).


#import "@preview/touying:0.7.4": *
#import "@preview/itemize:0.2.0" as el
#import "@preview/showybox:2.0.4": showybox

#let defbox(self: none, title: none, width: none, body) = context {
  layout(size => [
    #let w = 0.0
    #if width == none { w = calc.min(size.width, measure(body).width) } else { w = width }
    #showybox(
      width: w,
      frame: (
        border-color: red,
        title-color: blue,
        body-color: green,
      ),
      title-style: (
        boxed-style: (
          anchor: (x: left, y: horizon),
        ),
        color: black,
        weight: "bold",
        // align: left,
      ),
      title: title,
      body + [ #w] + [ #size.width] + [ #measure(body).width],
    )
  ])
}
#import themes.simple: *

#show: simple-theme.with(aspect-ratio: "16-9")

= Test

== Slide 1


#defbox(
  title: "Short",
  "A short thing",
)

== Slide 2

#defbox(
  title: "Long",
  lorem(20),
)

== Slide 3

#defbox(
  title: "A title wider than the body",
  lorem(2),
)

== Slide 4

#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(
    title: "Grid 1 with items",
    [
      - #lorem(5)
      - #lorem(5)
      - #lorem(5)
    ],
  ),
  defbox(
    title: "Grid 2 with image",
    align(horizon + center)[
      #image("/plots/XS_absorbers.png", fit: "contain", width: 85%)
    ],
  ),
)

== Slide 5

Image on previous slide is not displayed (no error warning in console)

== Slide 6: grid with items

#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(title: "Maxwell spectrum", [
    - $χ(E) d(E) = (2π)/(π T)^(3/2) sqrt(E) exp(-E/T)d(E)$
    - $dash(E)_text("fiss") = 3/2 T$
    - $hat(E)_text("fiss") = 1/2 T$
  ]),
  defbox(title: "Cranberg spectrum", [
    - $χ(E) d(E) = (2 exp(- (A B)/4)) / sqrt(π A^3 B) exp(- E/A) sinh(sqrt(B E)) d(E)$
    - $dash(E)_text("fiss") = (A^2 B +6A)/4$
    - $hat(E)_text("fiss") = …$
  ]),
)

== Slide 6: grid with items but now with _itemize_ package

#show: el.default-enum-list
#show: el.default-enum-list.with(
  fill: (red, auto),
  item-spacing: 1.1em,
  size: (1em, 0.9em, 0.9em, 0.9em),
  body-format: (style: (size: (1em, 0.9em, 0.9em, 0.9em))),
)


#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(title: "Maxwell spectrum", [
    - $χ(E) d(E) = (2π)/(π T)^(3/2) sqrt(E) exp(-E/T)d(E)$
    - $dash(E)_text("fiss") = 3/2 T$
    - $hat(E)_text("fiss") = 1/2 T$

  ]),
  defbox(title: "Cranberg spectrum", [
    - $χ(E) d(E) = (2 exp(- (A B)/4)) / sqrt(π A^3 B) exp(- E/A) sinh(sqrt(B E)) d(E)$
    - $dash(E)_text("fiss") = (A^2 B +6A)/4$
    - $hat(E)_text("fiss") = …$

  ]),
)

Your MWE still has a call to image() with a file that we don’t have. That’s easy enough though:

#image("/plots/XS_absorbers.png", fit: "contain", width: 85%)
//Becomes
#rect(width: 85%, height: 85%, stroke: 1pt)[Image]

The reason defbox() fails with the image is because the image wants to be 85% the size of its parent, but your function wants to set itself to the size of its content (the image). Apparently Typst chooses 0pt as the final size. Adding a check for this produces results that seem good to my eye, but of course you will have to be the judge.

//Added after w is set by the existing logic
if w == 0pt { w = 100% }

This change also gets good results for the itemize package case:

Full Code
#import "@preview/touying:0.7.4": *
#import "@preview/itemize:0.2.0" as el
#import "@preview/showybox:2.0.4": showybox

#let defbox(self: none, title: none, width: none, body) = context {
  layout(size => [
    #let w = 0.0
    #if width == none { w = calc.min(size.width, measure(body).width) } else { w = width }
    #if w == 0pt { w = 100% }
    #showybox(
      width: w,
      frame: (
        border-color: red,
        title-color: blue,
        body-color: green,
      ),
      title-style: (
        boxed-style: (
          anchor: (x: left, y: horizon),
        ),
        color: black,
        weight: "bold",
        // align: left,
      ),
      title: title,
      body + [ #w] + [ #size.width] + [ #measure(body).width],
    )
  ])
}
#import themes.simple: *

#show: simple-theme.with(aspect-ratio: "16-9")

= Test

== Slide 1


#defbox(
  title: "Short",
  "A short thing",
)

== Slide 2

#defbox(
  title: "Long",
  lorem(20),
)

== Slide 3

#defbox(
  title: "A title wider than the body",
  lorem(2),
)

== Slide 4

#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(
    title: "Grid 1 with items",
    [
      - #lorem(5)
      - #lorem(5)
      - #lorem(5)
    ],
  ),
  defbox(
    title: "Grid 2 with image",
    align(horizon + center)[
      #rect(width: 85%, height: 85%, stroke: 1pt)[Image]
    ],
  ),
)

== Slide 5

Image on previous slide is not displayed (no error warning in console)

== Slide 6: grid with items

#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(title: "Maxwell spectrum", [
    - $χ(E) d(E) = (2π)/(π T)^(3/2) sqrt(E) exp(-E/T)d(E)$
    - $dash(E)_text("fiss") = 3/2 T$
    - $hat(E)_text("fiss") = 1/2 T$
  ]),
  defbox(title: "Cranberg spectrum", [
    - $χ(E) d(E) = (2 exp(- (A B)/4)) / sqrt(π A^3 B) exp(- E/A) sinh(sqrt(B E)) d(E)$
    - $dash(E)_text("fiss") = (A^2 B +6A)/4$
    - $hat(E)_text("fiss") = …$
  ]),
)

== Slide 6: grid with items but now with _itemize_ package

#show: el.default-enum-list
#show: el.default-enum-list.with(
  fill: (red, auto),
  item-spacing: 1.1em,
  size: (1em, 0.9em, 0.9em, 0.9em),
  body-format: (style: (size: (1em, 0.9em, 0.9em, 0.9em))),
)


#grid(
  columns: (1fr, 1fr),
  inset: (x: 10pt),
  defbox(title: "Maxwell spectrum", [
    - $χ(E) d(E) = (2π)/(π T)^(3/2) sqrt(E) exp(-E/T)d(E)$
    - $dash(E)_text("fiss") = 3/2 T$
    - $hat(E)_text("fiss") = 1/2 T$

  ]),
  defbox(title: "Cranberg spectrum", [
    - $χ(E) d(E) = (2 exp(- (A B)/4)) / sqrt(π A^3 B) exp(- E/A) sinh(sqrt(B E)) d(E)$
    - $dash(E)_text("fiss") = (A^2 B +6A)/4$
    - $hat(E)_text("fiss") = …$

  ]),
)

I’ve also got a non-critical note about the way you are using brackets in your function.
The call to layout() is done like this:

  layout(size => [
    #let w = 0.0
    #if width == none { w = calc.min(size.width, measure(body).width) } else { w = width }
    #showybox(
      //...
    )
  ])

Since you are never placing any content or markup within those []s, it’s possible to do it this way:

  layout(size => {
    let w = 0.0
    if width == none { w = calc.min(size.width, measure(body).width) } else { w = width }
    showybox(
      //...
    )
  })

Is it a small change? yes, but it makes fore cleaner code in my opinion.
Will it affect layout? possibly maybe, probably not.

3 Likes