diff --git a/NEWS.md b/NEWS.md index 79ec3d92fb..747eebf6bb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,9 @@ * The `scale_name` argument in `continuous_scale()`, `discrete_scale()` and `binned_scale()` is soft-deprecated (@teunbrand, #1312). +* 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) diff --git a/R/theme-elements.R b/R/theme-elements.R index 044df52406..ba275e398c 100644 --- a/R/theme-elements.R +++ b/R/theme-elements.R @@ -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"), @@ -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")), @@ -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"), diff --git a/R/theme.R b/R/theme.R index 2bfdd5a835..33cf0244cd 100644 --- a/R/theme.R +++ b/R/theme.R @@ -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) diff --git a/_pkgdown.yml b/_pkgdown.yml index 8276a5ded9..7dbedc3062 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -285,7 +285,7 @@ news: navbar: structure: - left: [reference, news, articles, extensions] + left: [intro, reference, news, articles, extensions] components: extensions: text: Extensions diff --git a/tests/testthat/test-theme.R b/tests/testthat/test-theme.R index 2ae6a2bef4..7bf37c90e1 100644 --- a/tests/testthat/test-theme.R +++ b/tests/testthat/test-theme.R @@ -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", {