Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example creating new theme elements #322

Open
teunbrand opened this issue Aug 10, 2022 · 0 comments
Open

Example creating new theme elements #322

teunbrand opened this issue Aug 10, 2022 · 0 comments

Comments

@teunbrand
Copy link

It was pointed out to me that the ggplot2 book might look for an example of extending theme elements,

>Maybe something about creating new theme elements?

I think I found a nice example that can be considered for the book. From R 4.2+, the {grid} package has added the option to do outline strokes for text. The example defines a new text element that draws outlined text instead of regular text.

For the class constructor, it shows how to (1) allow alternate spellings of color/colour (2) how to structure a class and (3) how to inherit from element_text.

library(ggplot2)
library(grid)
library(rlang)

element_text_outline <- function(
  family = NULL, face = NULL, colour = NULL, fill = NULL,
  size = NULL, hjust = NULL, vjust = NULL, angle = NULL, 
  lineheight = NULL, linewidth = NULL,
  color = NULL, margin = NULL, inherit.blank = FALSE
) {
  # Allow both spellings of colo(u)r
  if (!is.null(color)) {
    colour <- color
  }
  
  structure(
    # The core of the class is a list of parameters
    list(
      family = family,
      face   = face,
      colour = colour,
      fill   = fill,
      size   = size,
      hjust  = hjust,
      vjust  = vjust,
      angle  = angle,
      lineheight = lineheight,
      linewidth  = linewidth,
      margin = margin,
      inherit.blank = inherit.blank
    ),
    # The class should inherit from one of the existing classes
    class = c("element_text_outline", "element_text", "element")
  )
}

The grob drawing code avoids getting into the nitty {grid}dy of margins and such by recycling the element_text's code with NextMethod(). It also shows the override rules that arguments provided to element_grob() should override those provided by the element itself.

element_grob.element_text_outline <- function(
    element, 
    # If we're using the parent's method, we can save ourselves
    # some space by using the ellipsis.
    ..., 
    fill = NULL,
    linewidth = NULL
) {
  # You can use the parent class' method to draw the basic grob
  text <- NextMethod()
  
  # Provided arguments should override the element's settings
  fill <- fill %||% element$fill
  linewidth <- linewidth %||% element$linewidth
  
  gp <- gpar(fill = fill, lwd = linewidth)
  
  # Wrap every textGrob inside a fillStrokeGrob
  text$children <- lapply(text$children, fillStrokeGrob, gp = gp)
  text
}

Finally, an example plot to show that the new element can be used in various places.

ggplot(mpg, aes(displ, cty, colour = factor(cyl))) +
  geom_point() +
  theme(
    axis.title.x = element_text_outline(size = 18),
    legend.text  = element_text_outline(
      size = 12, linewidth = 0.5,
      colour = "red", fill = "gold"
    )
  )

Created on 2022-08-10 by the reprex package (v2.0.1)

One drawback is that {ragg} does not (yet) support these R 4.2 features, so this reprex was rendered with:

#+ setup, include=FALSE
knitr::opts_chunk$set(dev.args = list(png = list(type = "cairo")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants