I'm developing a ggplot2 extension that needs to resolve a user-supplied element_*() object as if it were a child of a specific theme element. I'd like to do this without permanently registering the element in the global element tree.
Use case:
My extension, let's call it supertable, adds tables to plots. Users can customize their appearance both via the theme (through custom registered elements like supertable.value) and via inline parameters:
supertable(
value_style = element_text(size = rel(2)),
title_style = element_text(margin = margin_part(b = 6),
line_style = element_line(color = "red")
)
The inline parameter should sit "above" the theme element in the inheritance chain — i.e., rel() and margin_part() should resolve against the fully inherited theme element value, exactly as they would for any normal parent-child relationship in the element tree.
The problem:
The only way to get full resolution (including rel(), margin_part(), and any future deferred-resolution mechanisms) is through calc_element(). merge_element() seems like it should help here, but it simply overwrites fields from the new element onto the old one without resolving deferred values like rel(). So calc_element() appears to be the only option, but it requires the element to be registered in the global tree via register_theme_elements(). So my current workaround is:
calc_element_with_override <- function(override, element, theme, el_class) {
if (is.null(override)) override <- el_class()
secret_name <- paste0(element, ".__override__")
register_theme_elements(
!!secret_name := el_class(),
element_tree = rlang::list2(
!!secret_name := el_def(el_class, inherit = element)
)
)
theme[[secret_name]] <- override
calc_element(secret_name, theme)
}
This works, but it permanently alters the global element tree as a side effect, which feels wrong.
What would help:
An exported function that resolves an element object against a parent element object. This would apply the same resolution logic as calc_element (resolving rel(), margin_part(), etc.) but without requiring the element to be registered globally. It could be called resolve_element.
I suspect this could also be useful for other extension authors who need to compute derived styles at render time. Thanks for your consideration.
I'm developing a ggplot2 extension that needs to resolve a user-supplied
element_*()object as if it were a child of a specific theme element. I'd like to do this without permanently registering the element in the global element tree.Use case:
My extension, let's call it supertable, adds tables to plots. Users can customize their appearance both via the theme (through custom registered elements like
supertable.value) and via inline parameters:The inline parameter should sit "above" the theme element in the inheritance chain — i.e.,
rel()andmargin_part()should resolve against the fully inherited theme element value, exactly as they would for any normal parent-child relationship in the element tree.The problem:
The only way to get full resolution (including
rel(),margin_part(), and any future deferred-resolution mechanisms) is throughcalc_element().merge_element()seems like it should help here, but it simply overwrites fields from the new element onto the old one without resolving deferred values likerel(). Socalc_element()appears to be the only option, but it requires the element to be registered in the global tree viaregister_theme_elements(). So my current workaround is:This works, but it permanently alters the global element tree as a side effect, which feels wrong.
What would help:
An exported function that resolves an element object against a parent element object. This would apply the same resolution logic as
calc_element(resolvingrel(),margin_part(), etc.) but without requiring the element to be registered globally. It could be calledresolve_element.I suspect this could also be useful for other extension authors who need to compute derived styles at render time. Thanks for your consideration.