From 49121958e89c50ce95de304583f17e2a9ce325b6 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 15 Apr 2025 15:23:25 +0200 Subject: [PATCH 1/6] allow for relative `spacing` in `redistribute_null_units()` --- R/guides-.R | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/R/guides-.R b/R/guides-.R index d96ef16074..d8c6a09801 100644 --- a/R/guides-.R +++ b/R/guides-.R @@ -933,26 +933,20 @@ redistribute_null_units <- function(units, spacing, margin, type = "width") { } # Get spacing between guides and margins in absolute units - size <- switch(type, width = convertWidth, height = convertHeight) - spacing <- size(spacing, "cm", valueOnly = TRUE) - spacing <- sum(rep(spacing, length(units) - 1)) + size <- switch(type, width = width_cm, height = height_cm) + spacing <- sum(rep(spacing, length.out = length(units) - 1)) margin <- switch(type, width = margin[c(2, 4)], height = margin[c(1, 3)]) - margin <- sum(size(margin, "cm", valueOnly = TRUE)) + margin <- sum(size(margin)) # Get the absolute parts of the unit - absolute <- vapply(units, function(u) { - u <- absolute.size(u) - u <- size(u, "cm", valueOnly = TRUE) - sum(u) - }, numeric(1)) - absolute_sum <- sum(absolute) + spacing + margin + absolute <- vapply(units, function(u) sum(size(absolute.size(u))), numeric(1)) + absolute_sum <- sum(absolute) + sum(size(spacing)) + margin # Get the null parts of the unit + num_null <- function(x) sum(as.numeric(x)[unitType(x) == "null"]) relative <- rep(0, length(units)) - relative[has_null] <- vapply(units[has_null], function(u) { - sum(as.numeric(u)[unitType(u) == "null"]) - }, numeric(1)) - relative_sum <- sum(relative) + relative[has_null] <- vapply(units[has_null], num_null, numeric(1)) + relative_sum <- sum(relative) + num_null(spacing) if (relative_sum == 0) { return(unit(absolute, "cm")) From cc33ac1781e64d1c5e7b9e9806315137d6e9aeb8 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 15 Apr 2025 15:29:21 +0200 Subject: [PATCH 2/6] null spacing counts as stretchy legends --- R/guides-.R | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/R/guides-.R b/R/guides-.R index d8c6a09801..29dd66545b 100644 --- a/R/guides-.R +++ b/R/guides-.R @@ -653,11 +653,14 @@ Guides <- ggproto( height = heightDetails(grobs[[i]])) ) } - - spacing <- convertWidth(theme$legend.spacing.x, "cm") + spacing <- theme$legend.spacing.x + stretch_spacing <- any(unitType(spacing) == "null") + if (!stretch_spacing) { + spacing <- convertWidth(spacing, "cm") + } heights <- unit(height_cm(lapply(heights, sum)), "cm") - if (stretch_x) { + if (stretch_x || stretch_spacing) { widths <- redistribute_null_units(widths, spacing, margin, "width") vp_width <- unit(1, "npc") } else { @@ -692,10 +695,14 @@ Guides <- ggproto( ) } - spacing <- convertHeight(theme$legend.spacing.y, "cm") + spacing <- theme$legend.spacing.y + stretch_spacing <- any(unitType(spacing) == "null") + if (!stretch_spacing) { + spacing <- convertWidth(spacing, "cm") + } widths <- unit(width_cm(lapply(widths, sum)), "cm") - if (stretch_y) { + if (stretch_y || stretch_spacing) { heights <- redistribute_null_units(heights, spacing, margin, "height") vp_height <- unit(1, "npc") } else { From 75464926dcc22b3bb67024344c1cdd6123a87680 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 15 Apr 2025 15:29:53 +0200 Subject: [PATCH 3/6] dismiss global margins when stretchy --- R/guides-.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/guides-.R b/R/guides-.R index 29dd66545b..61072f4e72 100644 --- a/R/guides-.R +++ b/R/guides-.R @@ -742,10 +742,10 @@ Guides <- ggproto( ) # Set global margin - if (stretch_x) { + if (stretch_x || stretch_spacing) { global_margin[c(2, 4)] <- unit(0, "cm") } - if (stretch_y) { + if (stretch_y || stretch_spacing) { global_margin[c(1, 3)] <- unit(0, "cm") } guides <- gtable_add_padding(guides, global_margin) From 0fc4483b05c8f99f9775be7b72409066263e43ed Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 15 Apr 2025 16:46:11 +0200 Subject: [PATCH 4/6] add test --- .../theme/horizontal-legends-placed-apart.svg | 100 ++++++++++++++++++ .../theme/vertical-legends-placed-apart.svg | 100 ++++++++++++++++++ tests/testthat/test-theme.R | 22 ++++ 3 files changed, 222 insertions(+) create mode 100644 tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg create mode 100644 tests/testthat/_snaps/theme/vertical-legends-placed-apart.svg diff --git a/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg b/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg new file mode 100644 index 0000000000..ae617e1df2 --- /dev/null +++ b/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1.0 +1.5 +2.0 +2.5 +3.0 + + + + + + + + + + +1.0 +1.5 +2.0 +2.5 +3.0 +x +y + + +a + + + + + + +a +b +c + +factor(x) + + + + + + +1 +2 +3 +horizontal legends placed apart + + diff --git a/tests/testthat/_snaps/theme/vertical-legends-placed-apart.svg b/tests/testthat/_snaps/theme/vertical-legends-placed-apart.svg new file mode 100644 index 0000000000..b9d674373a --- /dev/null +++ b/tests/testthat/_snaps/theme/vertical-legends-placed-apart.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1.0 +1.5 +2.0 +2.5 +3.0 + + + + + + + + + + +1.0 +1.5 +2.0 +2.5 +3.0 +x +y + + +a + + + + + + +a +b +c + +factor(x) + + + + + + +1 +2 +3 +vertical legends placed apart + + diff --git a/tests/testthat/test-theme.R b/tests/testthat/test-theme.R index 10ef91cf95..18963207a1 100644 --- a/tests/testthat/test-theme.R +++ b/tests/testthat/test-theme.R @@ -1051,3 +1051,25 @@ test_that("legend margins are correct when using relative key sizes", { expect_doppelganger("stretched horizontal legends", horizontal) }) + +test_that("legends are placed correctly when using stretchy spacing", { + + df <- data.frame(x = 1:3, y = 1:3, a = letters[1:3]) + + p <- ggplot(df, aes(x, y, colour = a, shape = factor(x))) + + geom_point() + + theme( + legend.box.background = element_rect(colour = "blue", fill = NA), + legend.background = element_rect(colour = "red", fill = NA) + ) + + expect_doppelganger( + "vertical legends placed apart", + p + theme(legend.position = "right", legend.spacing.y = unit(1, "null")) + ) + + expect_doppelganger( + "horizontal legends placed apart", + p + theme(legend.position = "right", legend.spacing.y = unit(1, "null")) + ) +}) From b404c927e9f63ea360d07f639fc47013a04acfbe Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 15 Apr 2025 17:29:07 +0200 Subject: [PATCH 5/6] add news bullet --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index cadba2e78e..fd48bb1172 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # ggplot2 (development version) +* The `theme(legend.spacing.{x/y})` setting now accepts `null`-units + (@teunbrand, #6417). * Facet gains a new method `setup_panel_params` to interact with the panel_params setted by Coord object (@Yunuuuu, #6397, #6380) * `position_fill()` avoids stacking observations of zero (@teunbrand, #6338) From 4ac627a368ee21cf8eb412ba3e9c7f7f2ff19e3c Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 19 May 2025 14:23:08 +0200 Subject: [PATCH 6/6] actually test the thing we say we're testing --- .../theme/horizontal-legends-placed-apart.svg | 140 +++++++++--------- tests/testthat/test-theme.R | 2 +- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg b/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg index ae617e1df2..edd353e814 100644 --- a/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg +++ b/tests/testthat/_snaps/theme/horizontal-legends-placed-apart.svg @@ -21,80 +21,80 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + -1.0 -1.5 -2.0 -2.5 -3.0 - - - - - - - - - - -1.0 -1.5 -2.0 -2.5 -3.0 -x -y - - -a - - - - - - -a -b -c - -factor(x) - - - - - - -1 -2 -3 +1.0 +1.5 +2.0 +2.5 +3.0 + + + + + + + + + + +1.0 +1.5 +2.0 +2.5 +3.0 +x +y + + +a + + + + + + +a +b +c + +factor(x) + + + + + + +1 +2 +3 horizontal legends placed apart diff --git a/tests/testthat/test-theme.R b/tests/testthat/test-theme.R index 18963207a1..9b68ae2b09 100644 --- a/tests/testthat/test-theme.R +++ b/tests/testthat/test-theme.R @@ -1070,6 +1070,6 @@ test_that("legends are placed correctly when using stretchy spacing", { expect_doppelganger( "horizontal legends placed apart", - p + theme(legend.position = "right", legend.spacing.y = unit(1, "null")) + p + theme(legend.position = "top", legend.spacing.x = unit(1, "null")) ) })