Skip to content

Commit

Permalink
<rel> and <unit> theme inheritance (#5403)
Browse files Browse the repository at this point in the history
* Allow rel in element tree

* Allow combining of rel elements

* Add test

* Add news bullet

* Fix test for pre-R4.0.0 units

* Also apply `rel` to numerics
  • Loading branch information
teunbrand authored Sep 12, 2023
1 parent 22ed73c commit 67bb3bb
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 12 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# ggplot2 (development version)

* In `theme()`, some elements can be specified with `rel()` to inherit from
`unit`-class objects in a relative fashion (@teunbrand, #3951).

* `stat_ydensity()` with incomplete groups calculates the default `width`
parameter more stably (@teunbrand, #5396)

Expand Down
24 changes: 12 additions & 12 deletions R/theme-elements.R
Original file line number Diff line number Diff line change
Expand Up @@ -448,12 +448,12 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
axis.text.y.left = el_def("element_text", "axis.text.y"),
axis.text.y.right = el_def("element_text", "axis.text.y"),
axis.ticks.length = el_def("unit"),
axis.ticks.length.x = el_def("unit", "axis.ticks.length"),
axis.ticks.length.x.top = el_def("unit", "axis.ticks.length.x"),
axis.ticks.length.x.bottom = el_def("unit", "axis.ticks.length.x"),
axis.ticks.length.y = el_def("unit", "axis.ticks.length"),
axis.ticks.length.y.left = el_def("unit", "axis.ticks.length.y"),
axis.ticks.length.y.right = el_def("unit", "axis.ticks.length.y"),
axis.ticks.length.x = el_def(c("unit", "rel"), "axis.ticks.length"),
axis.ticks.length.x.top = el_def(c("unit", "rel"), "axis.ticks.length.x"),
axis.ticks.length.x.bottom = el_def(c("unit", "rel"), "axis.ticks.length.x"),
axis.ticks.length.y = el_def(c("unit", "rel"), "axis.ticks.length"),
axis.ticks.length.y.left = el_def(c("unit", "rel"), "axis.ticks.length.y"),
axis.ticks.length.y.right = el_def(c("unit", "rel"), "axis.ticks.length.y"),
axis.ticks.x = el_def("element_line", "axis.ticks"),
axis.ticks.x.top = el_def("element_line", "axis.ticks.x"),
axis.ticks.x.bottom = el_def("element_line", "axis.ticks.x"),
Expand All @@ -470,11 +470,11 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
legend.background = el_def("element_rect", "rect"),
legend.margin = el_def("margin"),
legend.spacing = el_def("unit"),
legend.spacing.x = el_def("unit", "legend.spacing"),
legend.spacing.y = el_def("unit", "legend.spacing"),
legend.spacing.x = el_def(c("unit", "rel"), "legend.spacing"),
legend.spacing.y = el_def(c("unit", "rel"), "legend.spacing"),
legend.key = el_def("element_rect", "rect"),
legend.key.height = el_def("unit", "legend.key.size"),
legend.key.width = el_def("unit", "legend.key.size"),
legend.key.height = el_def(c("unit", "rel"), "legend.key.size"),
legend.key.width = el_def(c("unit", "rel"), "legend.key.size"),
legend.text = el_def("element_text", "text"),
legend.title = el_def("element_text", "title"),
legend.position = el_def(c("character", "numeric", "integer")),
Expand All @@ -489,8 +489,8 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
panel.background = el_def("element_rect", "rect"),
panel.border = el_def("element_rect", "rect"),
panel.spacing = el_def("unit"),
panel.spacing.x = el_def("unit", "panel.spacing"),
panel.spacing.y = el_def("unit", "panel.spacing"),
panel.spacing.x = el_def(c("unit", "rel"), "panel.spacing"),
panel.spacing.y = el_def(c("unit", "rel"), "panel.spacing"),
panel.grid.major.x = el_def("element_line", "panel.grid.major"),
panel.grid.major.y = el_def("element_line", "panel.grid.major"),
panel.grid.minor.x = el_def("element_line", "panel.grid.minor"),
Expand Down
14 changes: 14 additions & 0 deletions R/theme.R
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,20 @@ combine_elements <- function(e1, e2) {
return(e2)
}

# Inheritance of rel objects
if (is.rel(e1)) {
# Both e1 and e2 are rel, give product as another rel
if (is.rel(e2)) {
return(rel(unclass(e1) * unclass(e2)))
}
# If e2 is a unit/numeric, return modified unit/numeric
# Note that unit objects are considered numeric
if (is.numeric(e2) || is.unit(e2)) {
return(unclass(e1) * e2)
}
return(e1)
}

# If neither of e1 or e2 are element_* objects, return e1
if (!inherits(e1, "element") && !inherits(e2, "element")) {
return(e1)
Expand Down
10 changes: 10 additions & 0 deletions tests/testthat/test-theme.R
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ test_that("calculating theme element inheritance works", {
e1 <- ggplot2:::calc_element("strip.text.x", theme)
e2 <- ggplot2:::calc_element("strip.text", theme)
expect_identical(e1, e2)

# Check that rel units are computed appropriately
theme <- theme_gray() +
theme(axis.ticks.length = unit(1, "cm"),
axis.ticks.length.x = rel(0.5),
axis.ticks.length.x.bottom = rel(4))

expect_equal(calc_element("axis.ticks.length.y.left", theme), unit(1, "cm"))
expect_equal(calc_element("axis.ticks.length.x.top", theme), unit(1, "cm") * 0.5)
expect_equal(calc_element("axis.ticks.length.x.bottom", theme), unit(1, "cm") * 0.5 * 4)
})

test_that("complete and non-complete themes interact correctly with each other", {
Expand Down

0 comments on commit 67bb3bb

Please sign in to comment.