Skip to content

Commit

Permalink
Layout top/bottom legends horizontally
Browse files Browse the repository at this point in the history
Fixes #1842
  • Loading branch information
hadley committed Oct 9, 2016
1 parent d919df3 commit 5b70f2a
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 41 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Expand Up @@ -126,6 +126,10 @@ There were also a number of other smaller changes
* Theme element inheritance is now easier to work with. Modification now
overrides default `element_blank` elements (#1555, #1557, #1565, #1567)

* Horizontal legends (i.e. legends on the top or bottom) are horizontally
aligned by default (#1842). Use `legend.box = "vertical"` to override the
default.

* `element_line()` now takes an `arrow` argument to specify arrows at the end of
lines (#1740)

Expand Down
53 changes: 28 additions & 25 deletions R/guides-.r
Expand Up @@ -45,8 +45,6 @@
#'
#' # position of guides
#'
#' p + theme(legend.position = "bottom", legend.box = "horizontal")
#'
#' # Set order for multiple guides
#' ggplot(mpg, aes(displ, cty)) +
#' geom_point(aes(size = hwy, colour = cyl, shape = drv)) +
Expand Down Expand Up @@ -93,32 +91,24 @@ update_guides <- function(p, guides) {
# arrange all ggrobs

build_guides <- function(scales, layers, default_mapping, position, theme, guides, labels) {

# set themes w.r.t. guides
# should these theme$legend.XXX be renamed to theme$guide.XXX ?

# by default, guide boxes are vertically aligned
theme$legend.box <- theme$legend.box %||% "vertical"

# size of key (also used for bar in colorbar guide)
theme$legend.key.width <- theme$legend.key.width %||% theme$legend.key.size
theme$legend.key.height <- theme$legend.key.height %||% theme$legend.key.size

# by default, direction of each guide depends on the position of the guide.
theme$legend.direction <-
theme$legend.direction %||%
if (length(position) == 1 && position %in% c("top", "bottom", "left", "right"))
switch(position[1], top = , bottom = "horizontal", left = , right = "vertical")
else
"vertical"

# justification of legend boxes
theme$legend.box.just <-
theme$legend.box.just %||%
if (length(position) == 1 && position %in% c("top", "bottom", "left", "right"))
switch(position, bottom = , top = c("center", "top"), left = , right = c("left", "top"))
else
c("center", "center")
# Layout of legends depends on their overall location
position <- legend_position(position)
if (position == "inside") {
theme$legend.box <- theme$legend.box %||% "vertical"
theme$legend.direction <- theme$legend.direction %||% "vertical"
theme$legend.box.just <- theme$legend.box.just %||% c("center", "center")
} else if (position == "vertical") {
theme$legend.box <- theme$legend.box %||% "vertical"
theme$legend.direction <- theme$legend.direction %||% "vertical"
theme$legend.box.just <- theme$legend.box.just %||% c("left", "top")
} else if (position == "horizontal") {
theme$legend.box <- theme$legend.box %||% "horizontal"
theme$legend.direction <- theme$legend.direction %||% "horizontal"
theme$legend.box.just <- theme$legend.box.just %||% c("center", "top")
}

# scales -> data for guides
gdefs <- guides_train(scales = scales, theme = theme, guides = guides, labels = labels)
Expand All @@ -140,6 +130,19 @@ build_guides <- function(scales, layers, default_mapping, position, theme, guide
grobs
}

# Simplify legend position to one of horizontal/vertical/inside
legend_position <- function(position) {
if (length(position) == 1) {
if (position %in% c("top", "bottom")) {
"horizontal"
} else {
"vertical"
}
} else {
"inside"
}
}

# validate guide object
validate_guide <- function(guide) {
# if guide is specified by character, then find the corresponding guide
Expand Down
10 changes: 3 additions & 7 deletions R/theme.r
Expand Up @@ -274,18 +274,14 @@ print.theme <- function(x, ...) utils::str(x)
#' # Position
#' p2 + theme(legend.position = "none")
#' p2 + theme(legend.justification = "top")
#'
#' p2 + theme(legend.position = "bottom")
#' p2 + theme(
#' legend.position = "bottom",
#' legend.box = "horizontal",
#' legend.justification = "left"
#' )
#'
#' # Or place inside the plot using relative coordinates between 0 and 1
#' # legend.justification sets the corner that the position refers to
#' p2 + theme(
#' legend.position = c(.95, .95),
#' legend.justification = c("right", "top"),
#' legend.box.just = "right",
#' legend.position = c(.95, .95),
#' legend.margin = margin(6, 6, 6, 6)
#' )
#'
Expand Down
2 changes: 0 additions & 2 deletions man/guides.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions man/theme.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5b70f2a

Please sign in to comment.