Skip to content

Commit 1803552

Browse files
authored
Subtheme functions (#5430)
* Draft subtheme * warn when elements are unknown * Document * add tests * Add news bullet * Tweak test * Add topic to pkgdown index * rename prefix to `theme_sub_*()` * cleanup merge debris
1 parent 97edd62 commit 1803552

File tree

8 files changed

+351
-0
lines changed

8 files changed

+351
-0
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ Collate:
274274
'theme.R'
275275
'theme-defaults.R'
276276
'theme-current.R'
277+
'theme-sub.R'
277278
'utilities-break.R'
278279
'utilities-grid.R'
279280
'utilities-help.R'

NAMESPACE

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,17 @@ export(theme_linedraw)
725725
export(theme_minimal)
726726
export(theme_replace)
727727
export(theme_set)
728+
export(theme_sub_axis)
729+
export(theme_sub_axis_bottom)
730+
export(theme_sub_axis_left)
731+
export(theme_sub_axis_right)
732+
export(theme_sub_axis_top)
733+
export(theme_sub_axis_x)
734+
export(theme_sub_axis_y)
735+
export(theme_sub_legend)
736+
export(theme_sub_panel)
737+
export(theme_sub_plot)
738+
export(theme_sub_strip)
728739
export(theme_test)
729740
export(theme_transparent)
730741
export(theme_update)

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# ggplot2 (development version)
22

3+
* New function family for setting parts of a theme. For example, you can now use
4+
`theme_sub_axis(line, text, ticks, ticks.length, line)` as a substitute for
5+
`theme(axis.line, axis.text, axis.ticks, axis.ticks.length, axis.line)`. This
6+
should allow slightly terser and more organised theme declarations
7+
(@teunbrand, #5301).
38
* `scale_{x/y}_discrete(continuous.limits)` is a new argument to control the
49
display range of discrete scales (@teunbrand, #4174, #6259).
510
* `geom_ribbon()` now appropriately warns about, and removes, missing values

R/theme-sub.R

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#' Shortcuts for theme settings
2+
#'
3+
#' This collection of functions serves as a shortcut for [`theme()`][theme] with
4+
#' shorter argument names. Besides the shorter arguments, it also helps in
5+
#' keeping theme declarations more organised.
6+
#'
7+
#' @eval subtheme_param_doc()
8+
#'
9+
#' @return A `theme`-class object that can be added to a plot.
10+
#' @name subtheme
11+
#'
12+
#' @examples
13+
#' # A standard plot
14+
#' p <- ggplot(mtcars, aes(disp, mpg, colour = drat)) +
15+
#' geom_point()
16+
#'
17+
#' red_text <- element_text(colour = "red")
18+
#' red_line <- element_line(colour = "red")
19+
#'
20+
#' # The theme settings below:
21+
#' p + theme(
22+
#' axis.title.x.bottom = red_text,
23+
#' axis.text.x.bottom = red_text,
24+
#' axis.line.x.bottom = red_line,
25+
#' axis.ticks.x.bottom = red_line
26+
#' )
27+
#'
28+
#' # Are equivalent to these less verbose theme settings
29+
#' p + theme_sub_axis_bottom(
30+
#' title = red_text,
31+
#' text = red_text,
32+
#' line = red_line,
33+
#' ticks = red_line
34+
#' )
35+
NULL
36+
37+
subtheme <- function(elements, prefix = "", suffix = "", call = caller_env()) {
38+
if (length(elements) < 1) {
39+
return(theme())
40+
}
41+
names(elements) <- paste0(prefix, names(elements), suffix)
42+
43+
extra <- setdiff(names(elements), names(get_element_tree()))
44+
if (length(extra) > 0) {
45+
cli::cli_warn(
46+
"Ignoring unknown {.fn theme} element{?s}: {.and {.field {extra}}}.",
47+
call = call
48+
)
49+
elements <- elements[setdiff(names(elements), extra)]
50+
}
51+
52+
exec(theme, !!!elements)
53+
}
54+
55+
#' @export
56+
#' @describeIn subtheme Theme specification for all axes.
57+
theme_sub_axis <- function(title, text, ticks, ticks.length, line) {
58+
subtheme(find_args(), "axis.")
59+
}
60+
61+
#' @export
62+
#' @describeIn subtheme Theme specification for both x axes.
63+
theme_sub_axis_x <- function(title, text, ticks, ticks.length, line) {
64+
subtheme(find_args(), "axis.", ".x")
65+
}
66+
67+
#' @export
68+
#' @describeIn subtheme Theme specification for both y axes.
69+
theme_sub_axis_y <- function(title, text, ticks, ticks.length, line) {
70+
subtheme(find_args(), "axis.", ".y")
71+
}
72+
73+
#' @export
74+
#' @describeIn subtheme Theme specification for the bottom x axis.
75+
theme_sub_axis_bottom <- function(title, text, ticks, ticks.length, line) {
76+
subtheme(find_args(), "axis.", ".x.bottom")
77+
}
78+
79+
#' @export
80+
#' @describeIn subtheme Theme specification for the top x axis.
81+
theme_sub_axis_top <- function(title, text, ticks, ticks.length, line) {
82+
subtheme(find_args(), "axis.", ".x.top")
83+
}
84+
85+
#' @export
86+
#' @describeIn subtheme Theme specification for the left y axis.
87+
theme_sub_axis_left <- function(title, text, ticks, ticks.length, line) {
88+
subtheme(find_args(), "axis.", ".y.left")
89+
}
90+
91+
#' @export
92+
#' @describeIn subtheme Theme specification for the right y axis.
93+
theme_sub_axis_right <- function(title, text, ticks, ticks.length, line) {
94+
subtheme(find_args(), "axis.", ".y.right")
95+
}
96+
97+
#' @export
98+
#' @describeIn subtheme Theme specification for the legend.
99+
theme_sub_legend <- function(background, margin, spacing, spacing.x, spacing.y,
100+
key, key.size, key.height, key.width, text, title,
101+
position, direction, justification, box, box.just,
102+
box.margin, box.background, box.spacing) {
103+
subtheme(find_args(), "legend.")
104+
}
105+
106+
#' @export
107+
#' @describeIn subtheme Theme specification for the panels.
108+
theme_sub_panel <- function(background, border, spacing, spacing.x, spacing.y,
109+
grid, grid.major, grid.minor, grid.major.x,
110+
grid.major.y, grid.minor.x, grid.minor.y, ontop) {
111+
subtheme(find_args(), "panel.")
112+
}
113+
114+
#' @export
115+
#' @describeIn subtheme Theme specification for the whole plot.
116+
theme_sub_plot <- function(background, title, title.position, subtitle, caption,
117+
caption.position, tag, tag.position, tag.location,
118+
margin) {
119+
subtheme(find_args(), "plot.")
120+
}
121+
122+
#' @export
123+
#' @describeIn subtheme Theme specification for facet strips.
124+
theme_sub_strip <- function(background, background.x, background.y, clip,
125+
placement, text, text.x, text.x.bottom, text.x.top,
126+
text.y, text.y.left, text.y.right,
127+
switch.pad.grid, switch.pad.wrap) {
128+
subtheme(find_args(), "strip.")
129+
}
130+
131+
subtheme_param_doc <- function() {
132+
funs <- list(
133+
theme_sub_axis, theme_sub_axis_x, theme_sub_axis_y, theme_sub_axis_bottom,
134+
theme_sub_axis_top, theme_sub_axis_left, theme_sub_axis_right, theme_sub_legend,
135+
theme_sub_panel, theme_sub_plot, theme_sub_strip
136+
)
137+
args <- sort(unique(unlist(lapply(funs, fn_fmls_names), use.names = FALSE)))
138+
paste0(
139+
"@param ",
140+
paste0(args, collapse = ","),
141+
" Arguments that are renamed and passed on to ",
142+
"\\code{\\link[=theme]{theme()}}."
143+
)
144+
}

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ reference:
186186
- theme
187187
- theme_bw
188188
- theme_update
189+
- subtheme
189190
- element_line
190191
- margin
191192

man/subtheme.Rd

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

tests/testthat/_snaps/theme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@
7777
`plot.tag.position` must be one of "topleft", "top", "topright", "left", "right", "bottomleft", "bottom", or "bottomright", not "test".
7878
i Did you mean "left"?
7979

80+
# subtheme functions rename arguments as intended
81+
82+
Ignoring unknown `theme()` elements: foo and bar.
83+
8084
# Theme validation behaves as expected
8185

8286
The `aspect.ratio` theme element must be a <numeric/integer> object.

tests/testthat/test-theme.R

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,32 @@ test_that("Theme elements are checked during build", {
514514
expect_snapshot_error(ggplotGrob(p))
515515
})
516516

517+
test_that("subtheme functions rename arguments as intended", {
518+
519+
line <- element_line(colour = "red")
520+
rect <- element_rect(colour = "red")
521+
522+
expect_equal(theme_sub_axis(ticks = line), theme(axis.ticks = line))
523+
expect_equal(theme_sub_axis_x(ticks = line), theme(axis.ticks.x = line))
524+
expect_equal(theme_sub_axis_y(ticks = line), theme(axis.ticks.y = line))
525+
expect_equal(theme_sub_axis_top(ticks = line), theme(axis.ticks.x.top = line))
526+
expect_equal(theme_sub_axis_bottom(ticks = line), theme(axis.ticks.x.bottom = line))
527+
expect_equal(theme_sub_axis_left(ticks = line), theme(axis.ticks.y.left = line))
528+
expect_equal(theme_sub_axis_right(ticks = line), theme(axis.ticks.y.right = line))
529+
expect_equal(theme_sub_legend(key = rect), theme(legend.key = rect))
530+
expect_equal(theme_sub_panel(border = rect), theme(panel.border = rect))
531+
expect_equal(theme_sub_plot(background = rect), theme(plot.background = rect))
532+
expect_equal(theme_sub_strip(background = rect), theme(strip.background = rect))
533+
534+
# Test rejection of unknown theme elements
535+
expect_snapshot_warning(
536+
expect_equal(
537+
subtheme(list(foo = 1, bar = 2, axis.line = line)),
538+
theme(axis.line = line)
539+
)
540+
)
541+
})
542+
517543
test_that("Theme validation behaves as expected", {
518544
tree <- get_element_tree()
519545
expect_silent(validate_element(1, "aspect.ratio", tree))

0 commit comments

Comments
 (0)