Skip to content

Commit 619d01c

Browse files
thomasp85karawoo
authored andcommitted
Keep element subclasses when adding (#2063)
* Use child element as base when merging theme elements * Add the correct element * Add merge_element generic and methods for all base classes * Check for direct inheritance * Add unit tests * Update news * Update roxygen * Only display most specific class in error message * dispatch on element class * Reformat line * More code formatting
1 parent c6623c3 commit 619d01c

File tree

5 files changed

+110
-8
lines changed

5 files changed

+110
-8
lines changed

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ S3method(limits,factor)
6565
S3method(limits,numeric)
6666
S3method(makeContent,labelgrob)
6767
S3method(makeContext,dotstackGrob)
68+
S3method(merge_element,default)
69+
S3method(merge_element,element)
6870
S3method(plot,ggplot)
6971
S3method(predictdf,default)
7072
S3method(predictdf,glm)
@@ -347,6 +349,7 @@ export(mean_cl_normal)
347349
export(mean_sdl)
348350
export(mean_se)
349351
export(median_hilow)
352+
export(merge_element)
350353
export(panel_cols)
351354
export(panel_rows)
352355
export(position_dodge)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# ggplot2 2.2.1.9000
22

3+
* Theme elements can now be subclassed. Add a `merge_element` method to control
4+
how properties are inherited from parent element. Add `element_grob` method
5+
to define how elements are rendered into grobs (@thomasp85, #1981).
6+
37
* Theme functions now have the optional parameters `base_line_size` and
48
`base_rect_size` to control the default sizes of line and rectangle elements
59
(@karawoo, #2176).

R/theme.r

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -409,14 +409,8 @@ add_theme <- function(t1, t2, t2name) {
409409
# If y is NULL, or a string or numeric vector, or is element_blank, just replace x
410410
x <- y
411411
} else {
412-
# If x is not NULL, then copy over the non-NULL properties from y
413-
# Get logical vector of non-NULL properties in y
414-
idx <- !vapply(y, is.null, logical(1))
415-
# Get the names of TRUE items
416-
idx <- names(idx[idx])
417-
418-
# Update non-NULL items
419-
x[idx] <- y[idx]
412+
# If x is not NULL, then merge into y
413+
x <- merge_element(y, x)
420414
}
421415

422416
# Assign it back to t1
@@ -538,6 +532,49 @@ calc_element <- function(element, theme, verbose = FALSE) {
538532
Reduce(combine_elements, parents, theme[[element]])
539533
}
540534

535+
#' Merge a parent element into a child element
536+
#'
537+
#' This is a generic and element classes must provide an implementation of this
538+
#' method
539+
#'
540+
#' @param new The child element in the theme hierarchy
541+
#' @param old The parent element in the theme hierarchy
542+
#' @return A modified version of \code{new} updated with the properties of
543+
#' \code{old}
544+
#' @keywords internal
545+
#' @export
546+
#' @examples
547+
#' new <- element_text(colour = "red")
548+
#' old <- element_text(colour = "blue", size = 10)
549+
#'
550+
#' # Adopt size but ignore colour
551+
#' merge_element(new, old)
552+
#'
553+
merge_element <- function(new, old) {
554+
UseMethod("merge_element")
555+
}
556+
#' @rdname merge_element
557+
#' @export
558+
merge_element.default <- function(new, old) {
559+
stop("No method for merging ", class(new)[1], " into ", class(old)[1], call. = FALSE)
560+
}
561+
#' @rdname merge_element
562+
#' @export
563+
merge_element.element <- function(new, old) {
564+
if (!inherits(new, class(old)[1])) {
565+
stop("Only elements of the same class can be merged", call. = FALSE)
566+
}
567+
# Override NULL properties of new with the values in old
568+
# Get logical vector of NULL properties in new
569+
idx <- vapply(new, is.null, logical(1))
570+
# Get the names of TRUE items
571+
idx <- names(idx[idx])
572+
573+
# Update non-NULL items
574+
new[idx] <- old[idx]
575+
576+
new
577+
}
541578

542579
# Combine the properties of two elements
543580
#

man/merge_element.Rd

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-theme.r

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,28 @@ test_that("All elements in complete themes have inherit.blank=TRUE", {
202202
expect_true(inherit_blanks(theme_void()))
203203
})
204204

205+
test_that("Elements can be merged", {
206+
text_base <- element_text(colour = "red", size = 10)
207+
expect_equal(
208+
merge_element(element_text(colour = "blue"), text_base),
209+
element_text(colour = "blue", size = 10)
210+
)
211+
rect_base <- element_rect(colour = "red", size = 10)
212+
expect_equal(
213+
merge_element(element_rect(colour = "blue"), rect_base),
214+
element_rect(colour = "blue", size = 10)
215+
)
216+
line_base <- element_line(colour = "red", size = 10)
217+
expect_equal(
218+
merge_element(element_line(colour = "blue"), line_base),
219+
element_line(colour = "blue", size = 10)
220+
)
221+
expect_error(
222+
merge_element(text_base, rect_base),
223+
"Only elements of the same class can be merged"
224+
)
225+
})
226+
205227

206228
# Visual tests ------------------------------------------------------------
207229

0 commit comments

Comments
 (0)