---
title: "Custom `ggplot2` arguments module"
author: "Maciej NasiƄski"
output: rmarkdown::html_vignette
runtime: shiny
vignette: >
  %\VignetteIndexEntry{Custom `ggplot2` arguments module}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

This vignette will guide you through implementation of custom `ggplot2::labs` and `ggplot2::theme` for `ggplot2` graphics
based modules.
We will enable 2 ways of updating `ggplot2::labs` and `ggplot2::theme` by the end users.
The `ggplot2` specification could be updated with the `teal.ggplot2_args` `options` variable or a `ggplot2_args`
argument in a `tm_g_*` module. We still take into account default specification set up by the module creator in the
server function, which has the lowest priority.

The implementation should consist of 5 steps:

1. Adding a `ggplot2_args` argument to the `tm_g_*` function and then its server function. The default should be set to
the `ggplot2_args(labs = list(), theme = list())` function for single plot.
and  `list(default = ggplot2_args(labs = list(), theme = list()))` multi-plot modules.
2. Adding a validation (e.g. `stopifnot` or `checkmate`) of the `ggplot2_args` argument to the `tm_*` function.
The validation is more complex for multi-plot modules, where the `ggplot2_args` could be a `list`.
The module creator has to provide a list of plots names, which should be validated at this step and added to the `param`
field in `roxygen2`.
For multi-plot modules the step `if (is_ggplot2_args) ggplot2_args <- list(default = ggplot2_args)` is recommended.
3. Aggregating and reducing all `ggplot2_args` sources with `resolve_ggplot2_args()`.
4. Usage of the `parse_ggplot2_args()` function which will parse inputs
to list of expressions.
5. Adding the created expression to the last chunk of a plot. `Reduce(function(x, y) call("+", x, y), list(...)`
function could be helpful at this step.

The `resolve_ggplot2_args()` function picks the first non NULL value for each argument, checking in order:

1. `ggplot2_args` argument provided by the end user.
For multi-plot case, per plot (`user_plot`) and then default (`user_default`) setup.
2. Global R variable (`options`), `teal.ggplot2_args`.
3. `module_plot` which is a developer setup.

### Additional topics

When a more complex `ggplot2` object has to be used inside the `ggplot2_args` function, then a `base::quote`
function would prevent an object expansion in Show R Code. For example the `ggplot2::element_text` function returns a
complex object, then we should use code like
`ggplot2_args(theme = list(plot.title = quote(ggplot2::element_text(size = 20))))` to prevent Show R Code
expansion.

If you get a `promise already under evaluation: recursive default argument reference or earlier problems?` error, then
probably your function argument has the same name as a function which is an input for it.
To solve the problem please use `::` to prefix it directly to a specific package, like
`new_fun <- function(ggplot2_args = ggplot2_args())`.

## Loading libraries and data

```{r warning=FALSE}
library(shiny)
library(ggplot2)
library(teal.widgets)

options("teal.ggplot2_args" = ggplot2_args(labs = list(caption = "Caption from options")))

user_ggplot2_args <- list(
  default = ggplot2_args(
    labs = list(title = "User default title"),
    theme = list(legend.position = "right", legend.direction = "vertical")
  ),
  plot1 = ggplot2_args(
    labs = list(title = "User title"),
    theme = list(legend.position = "right", legend.direction = "vertical")
  )
)

ui <- fluidPage(
  shinyjs::useShinyjs(),
  tags$div(plotOutput("plot1"))
)

server <- function(input, output, session) {
  dev_ggplot2_args <- ggplot2_args(
    labs = list(subtitle = "Dev substitle"),
    theme = list(legend.position = "none")
  )

  f_ggplot2_expr <- parse_ggplot2_args(
    resolve_ggplot2_args(
      user_plot = user_ggplot2_args$plot1,
      user_default = user_ggplot2_args$default,
      module_plot = dev_ggplot2_args
    )
  )

  plot_expr <- substitute(
    expr = {
      gg <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Length, color = Species)) +
        geom_point() +
        ggplot_expr_labs +
        ggplot_expr_theme
      print(gg)
    },
    env = list(ggplot_expr_labs = f_ggplot2_expr$labs, ggplot_expr_theme = f_ggplot2_expr$theme)
  )
  print(plot_expr)
  output$plot1 <- renderPlot(eval(plot_expr))
}

if (interactive()) {
  shinyApp(ui, server)
}
```