|
| 1 | +#' Custom guides |
| 2 | +#' |
| 3 | +#' This is a special guide that can be used to display any graphical object |
| 4 | +#' (grob) along with the regular guides. This guide has no associated scale. |
| 5 | +#' |
| 6 | +#' @param grob A grob to display. |
| 7 | +#' @param width,height The allocated width and height to display the grob, given |
| 8 | +#' in [grid::unit()]s. |
| 9 | +#' @param title A character string or expression indicating the title of guide. |
| 10 | +#' If `NULL` (default), no title is shown. |
| 11 | +#' @param title.position A character string indicating the position of a title. |
| 12 | +#' One of `"top"` (default), `"bottom"`, `"left"` or `"right"`. |
| 13 | +#' @param margin Margins around the guide. See [margin()] for more details. If |
| 14 | +#' `NULL` (default), margins are taken from the `legend.margin` theme setting. |
| 15 | +#' @param position Currently not in use. |
| 16 | +#' @inheritParams guide_legend |
| 17 | +#' |
| 18 | +#' @export |
| 19 | +#' |
| 20 | +#' @examples |
| 21 | +#' # A standard plot |
| 22 | +#' p <- ggplot(mpg, aes(displ, hwy)) + |
| 23 | +#' geom_point() |
| 24 | +#' |
| 25 | +#' # Define a graphical object |
| 26 | +#' circle <- grid::circleGrob() |
| 27 | +#' |
| 28 | +#' # Rendering a grob as a guide |
| 29 | +#' p + guides(custom = guide_custom(circle, title = "My circle")) |
| 30 | +#' |
| 31 | +#' # Controlling the size of the grob defined in relative units |
| 32 | +#' p + guides(custom = guide_custom( |
| 33 | +#' circle, title = "My circle", |
| 34 | +#' width = unit(2, "cm"), height = unit(2, "cm")) |
| 35 | +#' ) |
| 36 | +#' |
| 37 | +#' # Size of grobs in absolute units is taken directly without the need to |
| 38 | +#' # set these manually |
| 39 | +#' p + guides(custom = guide_custom( |
| 40 | +#' title = "My circle", |
| 41 | +#' grob = grid::circleGrob(r = unit(1, "cm")) |
| 42 | +#' )) |
| 43 | +guide_custom <- function( |
| 44 | + grob, width = grobWidth(grob), height = grobHeight(grob), |
| 45 | + title = NULL, title.position = "top", margin = NULL, |
| 46 | + position = waiver(), order = 0 |
| 47 | +) { |
| 48 | + check_object(grob, is.grob, "a {.cls grob} object") |
| 49 | + check_object(width, is.unit, "a {.cls unit} object") |
| 50 | + check_object(height, is.unit, "a {.cls unit} object") |
| 51 | + check_object(margin, is.margin, "a {.cls margin} object", allow_null = TRUE) |
| 52 | + if (length(width) != 1) { |
| 53 | + cli::cli_abort("{.arg width} must be a single {.cls unit}, not a unit vector.") |
| 54 | + } |
| 55 | + if (length(height) != 1) { |
| 56 | + cli::cli_abort("{.arg height} must be a single {.cls unit}, not a unit vector.") |
| 57 | + } |
| 58 | + title.position <- arg_match0(title.position, .trbl) |
| 59 | + |
| 60 | + new_guide( |
| 61 | + grob = grob, |
| 62 | + width = width, |
| 63 | + height = height, |
| 64 | + title = title, |
| 65 | + title.position = title.position, |
| 66 | + margin = margin, |
| 67 | + hash = hash(list(title, grob)), # hash is already known |
| 68 | + position = position, |
| 69 | + order = order, |
| 70 | + available_aes = "any", |
| 71 | + super = GuideCustom |
| 72 | + ) |
| 73 | +} |
| 74 | + |
| 75 | +#' @rdname ggplot2-ggproto |
| 76 | +#' @format NULL |
| 77 | +#' @usage NULL |
| 78 | +#' @export |
| 79 | +GuideCustom <- ggproto( |
| 80 | + "GuideCustom", Guide, |
| 81 | + |
| 82 | + params = c(Guide$params, list( |
| 83 | + grob = NULL, width = NULL, height = NULL, |
| 84 | + margin = NULL, |
| 85 | + title = NULL, |
| 86 | + title.position = "top" |
| 87 | + )), |
| 88 | + |
| 89 | + hashables = exprs(title, grob), |
| 90 | + |
| 91 | + elements = list( |
| 92 | + background = "legend.background", |
| 93 | + theme.margin = "legend.margin", |
| 94 | + theme.title = "legend.title" |
| 95 | + ), |
| 96 | + |
| 97 | + train = function(...) { |
| 98 | + params |
| 99 | + }, |
| 100 | + |
| 101 | + transform = function(...) { |
| 102 | + params |
| 103 | + }, |
| 104 | + |
| 105 | + override_elements = function(params, elements, theme) { |
| 106 | + elements$title <- elements$theme.title |
| 107 | + elements$margin <- params$margin %||% elements$theme.margin |
| 108 | + elements |
| 109 | + }, |
| 110 | + |
| 111 | + draw = function(self, theme, position = NULL, direction = NULL, |
| 112 | + params = self$params) { |
| 113 | + |
| 114 | + # Render title |
| 115 | + elems <- self$setup_elements(params, self$elements, theme) |
| 116 | + elems <- self$override_elements(params, elems, theme) |
| 117 | + if (!is.waive(params$title) && !is.null(params$title)) { |
| 118 | + title <- self$build_title(params$title, elems, params) |
| 119 | + } else { |
| 120 | + title <- zeroGrob() |
| 121 | + } |
| 122 | + title.position <- params$title.position |
| 123 | + if (is.zero(title)) { |
| 124 | + title.position <- "none" |
| 125 | + } |
| 126 | + |
| 127 | + width <- convertWidth(params$width, "cm") |
| 128 | + height <- convertHeight(params$height, "cm") |
| 129 | + gt <- gtable(widths = width, heights = height) |
| 130 | + gt <- gtable_add_grob(gt, params$grob, t = 1, l = 1, clip = "off") |
| 131 | + |
| 132 | + if (params$title.position == "top") { |
| 133 | + gt <- gtable_add_rows(gt, elems$margin[1], pos = 0) |
| 134 | + gt <- gtable_add_rows(gt, unit(height_cm(title), "cm"), pos = 0) |
| 135 | + gt <- gtable_add_grob(gt, title, t = 1, l = 1, name = "title", clip = "off") |
| 136 | + } else if (params$title.position == "bottom") { |
| 137 | + gt <- gtable_add_rows(gt, elems$margin[3], pos = -1) |
| 138 | + gt <- gtable_add_rows(gt, unit(height_cm(title), "cm"), pos = -1) |
| 139 | + gt <- gtable_add_grob(gt, title, t = -1, l = 1, name = "title", clip = "off") |
| 140 | + } else if (params$title.position == "left") { |
| 141 | + gt <- gtable_add_cols(gt, elems$margin[4], pos = 0) |
| 142 | + gt <- gtable_add_cols(gt, unit(width_cm(title), "cm"), pos = 0) |
| 143 | + gt <- gtable_add_grob(gt, title, t = 1, l = 1, name = "title", clip = "off") |
| 144 | + } else if (params$title.position == "right") { |
| 145 | + gt <- gtable_add_cols(gt, elems$margin[2], pos = -1) |
| 146 | + gt <- gtable_add_cols(gt, unit(width_cm(title), "cm"), pos = 0) |
| 147 | + gt <- gtable_add_grob(gt, title, t = 1, l = -1, name = "title", clip = "off") |
| 148 | + } |
| 149 | + gt <- gtable_add_padding(gt, elems$margin) |
| 150 | + |
| 151 | + background <- element_grob(elems$background) |
| 152 | + gt <- gtable_add_grob( |
| 153 | + gt, background, |
| 154 | + t = 1, l = 1, r = -1, b = -1, |
| 155 | + z = -Inf, clip = "off" |
| 156 | + ) |
| 157 | + gt |
| 158 | + } |
| 159 | +) |
0 commit comments