@@ -262,8 +262,8 @@ Guides <- ggproto(
262
262
263
263
# # Building ------------------------------------------------------------------
264
264
265
- # The `Guides$build()` method is called in ggplotGrob (plot-build.R) and makes
266
- # the guide box for *non-position* scales .
265
+ # The `Guides$build()` method is called in ggplot_build (plot-build.R) and
266
+ # collects all information needed from the plot .
267
267
# Note that position scales are handled in `Coord`s, which have their own
268
268
# procedures to do equivalent steps.
269
269
#
@@ -283,12 +283,7 @@ Guides <- ggproto(
283
283
# 3. Guides$process_layers()
284
284
# process layer information and generate geom info.
285
285
#
286
- # 4. Guides$draw()
287
- # generate guide grob from each guide object
288
- # one guide grob for one guide object
289
- #
290
- # 5. Guides$assemble()
291
- # arrange all guide grobs
286
+ # The resulting guide is then drawn in ggplot_gtable
292
287
293
288
build = function (self , scales , layers , labels , layer_data ) {
294
289
@@ -476,104 +471,201 @@ Guides <- ggproto(
476
471
invisible ()
477
472
},
478
473
479
- # Loop over every guide, let them draw their grobs
480
- draw = function (self , theme , position , direction ) {
481
- Map(
482
- function (guide , params ) guide $ draw(theme , position , direction , params ),
483
- guide = self $ guides ,
484
- params = self $ params
485
- )
486
- },
487
-
474
+ # The `Guides$assemble()` method is called in ggplot_gtable (plot-build.R) and
475
+ # applies the styling from the theme to render each guide and package them
476
+ # into guide boxes.
477
+ #
478
+ # The procedure is as follows
479
+ #
480
+ # 1. Guides$draw()
481
+ # for every guide object, draw one grob,
482
+ # then group the grobs in a list per position
483
+ #
484
+ # 2. Guides$package_box()
485
+ # for every position, collect all individual guides and arrange them
486
+ # into a guide box which will be inserted into the main gtable
488
487
# Combining multiple guides in a guide box
489
- assemble = function (self , theme , position ) {
488
+ assemble = function (self , theme ) {
490
489
491
490
if (length(self $ guides ) < 1 ) {
492
491
return (zeroGrob())
493
492
}
494
493
495
- position <- legend_position(position )
496
- if (position == " none" ) {
494
+ default_position <- theme $ legend.position %|| % " right"
495
+ if (length(default_position ) == 2 ) {
496
+ default_position <- " inside"
497
+ }
498
+ if (default_position == " none" ) {
497
499
return (zeroGrob())
498
500
}
499
- default_direction <- if (position == " inside" ) " vertical" else position
500
501
501
- theme $ legend.key.width <- theme $ legend.key.width %|| % theme $ legend.key.size
502
- theme $ legend.key.height <- theme $ legend.key.height %|| % theme $ legend.key.size
503
- theme $ legend.box <- theme $ legend.box %|| % default_direction
504
- theme $ legend.direction <- theme $ legend.direction %|| % default_direction
505
- theme $ legend.box.just <- theme $ legend.box.just %|| % switch (
506
- position ,
507
- inside = c(" center" , " center" ),
508
- vertical = c(" left" , " top" ),
509
- horizontal = c(" center" , " top" )
510
- )
502
+ # Populate key sizes
503
+ theme $ legend.key.width <- calc_element(" legend.key.width" , theme )
504
+ theme $ legend.key.height <- calc_element(" legend.key.height" , theme )
511
505
512
- grobs <- self $ draw(theme , position , theme $ legend.direction )
506
+ grobs <- self $ draw(theme , default_position , theme $ legend.direction )
513
507
if (length(grobs ) < 1 ) {
514
508
return (zeroGrob())
515
509
}
516
510
grobs <- grobs [order(names(grobs ))]
517
511
518
512
# Set spacing
519
- theme $ legend.spacing <- theme $ legend.spacing %|| % unit(0.5 , " lines" )
520
- theme $ legend.spacing.y <- theme $ legend.spacing.y %|| % theme $ legend.spacing
521
- theme $ legend.spacing.x <- theme $ legend.spacing.x %|| % theme $ legend.spacing
513
+ theme $ legend.spacing <- theme $ legend.spacing %|| % unit(0.5 , " lines" )
514
+ theme $ legend.spacing.y <- calc_element(" legend.spacing.y" , theme )
515
+ theme $ legend.spacing.x <- calc_element(" legend.spacing.x" , theme )
516
+
517
+ Map(
518
+ grobs = grobs ,
519
+ position = names(grobs ),
520
+ self $ package_box ,
521
+ MoreArgs = list (theme = theme )
522
+ )
523
+ },
524
+
525
+ # Render the guides into grobs
526
+ draw = function (self , theme ,
527
+ default_position = " right" ,
528
+ direction = NULL ,
529
+ params = self $ params ,
530
+ guides = self $ guides ) {
531
+ positions <- vapply(
532
+ params ,
533
+ function (p ) p $ position [1 ] %|| % default_position ,
534
+ character (1 )
535
+ )
536
+ positions <- factor (positions , levels = c(.trbl , " inside" ))
537
+
538
+ directions <- rep(direction %|| % " vertical" , length(positions ))
539
+ if (is.null(direction )) {
540
+ directions [positions %in% c(" top" , " bottom" )] <- " horizontal"
541
+ }
542
+
543
+ grobs <- vector(" list" , length(guides ))
544
+ for (i in seq_along(grobs )) {
545
+ grobs [[i ]] <- guides [[i ]]$ draw(
546
+ theme = theme , position = as.character(positions [i ]),
547
+ direction = directions [i ], params = params [[i ]]
548
+ )
549
+ }
550
+ split(grobs , positions )
551
+ },
552
+
553
+ package_box = function (grobs , position , theme ) {
554
+
555
+ if (is.zero(grobs ) || length(grobs ) == 0 ) {
556
+ return (zeroGrob())
557
+ }
558
+
559
+ # Determine default direction
560
+ direction <- switch (
561
+ position ,
562
+ inside = , left = , right = " vertical" ,
563
+ top = , bottom = " horizontal"
564
+ )
565
+
566
+ # Populate missing theme arguments
567
+ theme $ legend.box <- theme $ legend.box %|| % direction
568
+ theme $ legend.box.just <- theme $ legend.box.just %|| % switch (
569
+ direction ,
570
+ vertical = c(" left" , " top" ),
571
+ horizontal = c(" center" , " top" )
572
+ )
522
573
523
574
# Measure guides
524
575
widths <- lapply(grobs , function (g ) sum(g $ widths ))
525
576
widths <- inject(unit.c(!!! widths ))
526
577
heights <- lapply(grobs , function (g ) sum(g $ heights ))
527
578
heights <- inject(unit.c(!!! heights ))
528
579
580
+ # Global justification of the complete legend box
581
+ global_just <- paste0(" legend.justification." , position )
582
+ global_just <- valid.just(calc_element(global_just , theme ))
583
+
584
+ if (position == " inside" ) {
585
+ # The position of inside legends are set by their justification
586
+ inside_position <- theme $ legend.position.inside %|| % global_just
587
+ global_xjust <- inside_position [1 ]
588
+ global_yjust <- inside_position [2 ]
589
+ global_margin <- margin()
590
+ } else {
591
+ global_xjust <- global_just [1 ]
592
+ global_yjust <- global_just [2 ]
593
+ # Legends to the side of the plot need a margin for justification
594
+ # relative to the plot panel
595
+ global_margin <- margin(
596
+ t = 1 - global_yjust , b = global_yjust ,
597
+ r = 1 - global_xjust , l = global_xjust ,
598
+ unit = " null"
599
+ )
600
+ }
601
+
529
602
# Set the justification of each legend within the legend box
530
603
# First value is xjust, second value is yjust
531
- just <- valid.just(theme $ legend.box.just )
532
- xjust <- just [1 ]
533
- yjust <- just [2 ]
604
+ box_just <- valid.just(theme $ legend.box.just )
605
+ box_xjust <- box_just [1 ]
606
+ box_yjust <- box_just [2 ]
534
607
535
608
# setting that is different for vertical and horizontal guide-boxes.
536
609
if (identical(theme $ legend.box , " horizontal" )) {
537
- # Set justification for each legend
610
+ # Set justification for each legend within the box
538
611
for (i in seq_along(grobs )) {
539
612
grobs [[i ]] <- editGrob(
540
613
grobs [[i ]],
541
- vp = viewport(x = xjust , y = yjust , just = c( xjust , yjust ) ,
614
+ vp = viewport(x = box_xjust , y = box_yjust , just = box_just ,
542
615
height = heightDetails(grobs [[i ]]))
543
616
)
544
617
}
618
+ spacing <- theme $ legend.spacing.x
619
+
620
+ # Set global justification
621
+ vp <- viewport(
622
+ x = global_xjust , y = global_yjust , just = global_just ,
623
+ height = max(heights ),
624
+ width = sum(widths , spacing * (length(grobs ) - 1L ))
625
+ )
545
626
546
- guides <- gtable_row(name = " guides" ,
547
- grobs = grobs ,
548
- widths = widths , height = max(heights ))
627
+ # Initialise gtable as legends in a row
628
+ guides <- gtable_row(
629
+ name = " guides" , grobs = grobs ,
630
+ widths = widths , height = max(heights ),
631
+ vp = vp
632
+ )
549
633
550
- # add space between the guide-boxes
551
- guides <- gtable_add_col_space(guides , theme $ legend. spacing.x )
634
+ # Add space between the guide-boxes
635
+ guides <- gtable_add_col_space(guides , spacing )
552
636
553
637
} else { # theme$legend.box == "vertical"
554
- # Set justification for each legend
638
+ # Set justification for each legend within the box
555
639
for (i in seq_along(grobs )) {
556
640
grobs [[i ]] <- editGrob(
557
641
grobs [[i ]],
558
- vp = viewport(x = xjust , y = yjust , just = c( xjust , yjust ) ,
642
+ vp = viewport(x = box_xjust , y = box_yjust , just = box_just ,
559
643
width = widthDetails(grobs [[i ]]))
560
644
)
561
645
}
646
+ spacing <- theme $ legend.spacing.y
647
+
648
+ # Set global justification
649
+ vp <- viewport(
650
+ x = global_xjust , y = global_yjust , just = global_just ,
651
+ height = sum(heights , spacing * (length(grobs ) - 1L )),
652
+ width = max(widths )
653
+ )
562
654
563
- guides <- gtable_col(name = " guides" ,
564
- grobs = grobs ,
565
- width = max(widths ), heights = heights )
655
+ # Initialise gtable as legends in a column
656
+ guides <- gtable_col(
657
+ name = " guides" , grobs = grobs ,
658
+ width = max(widths ), heights = heights ,
659
+ vp = vp
660
+ )
566
661
567
- # add space between the guide-boxes
568
- guides <- gtable_add_row_space(guides , theme $ legend. spacing.y )
662
+ # Add space between the guide-boxes
663
+ guides <- gtable_add_row_space(guides , spacing )
569
664
}
570
665
571
666
# Add margins around the guide-boxes.
572
667
margin <- theme $ legend.box.margin %|| % margin()
573
- guides <- gtable_add_cols(guides , margin [4 ], pos = 0 )
574
- guides <- gtable_add_cols(guides , margin [2 ], pos = ncol(guides ))
575
- guides <- gtable_add_rows(guides , margin [1 ], pos = 0 )
576
- guides <- gtable_add_rows(guides , margin [3 ], pos = nrow(guides ))
668
+ guides <- gtable_add_padding(guides , margin )
577
669
578
670
# Add legend box background
579
671
background <- element_grob(theme $ legend.box.background %|| % element_blank())
@@ -584,6 +676,10 @@ Guides <- ggproto(
584
676
z = - Inf , clip = " off" ,
585
677
name = " legend.box.background"
586
678
)
679
+
680
+ # Set global margin
681
+ guides <- gtable_add_padding(guides , global_margin )
682
+
587
683
guides $ name <- " guide-box"
588
684
guides
589
685
},
0 commit comments