@@ -471,7 +471,7 @@ Guides <- ggproto(
471
471
# for every position, collect all individual guides and arrange them
472
472
# into a guide box which will be inserted into the main gtable
473
473
# Combining multiple guides in a guide box
474
- assemble = function (self , theme ) {
474
+ assemble = function (self , theme , params = self $ params , guides = self $ guides ) {
475
475
476
476
if (length(self $ guides ) < 1 ) {
477
477
return (zeroGrob())
@@ -485,42 +485,95 @@ Guides <- ggproto(
485
485
return (zeroGrob())
486
486
}
487
487
488
+ # extract the guide position
489
+ positions <- vapply(
490
+ params ,
491
+ function (p ) p $ position [1 ] %|| % default_position ,
492
+ character (1 ), USE.NAMES = FALSE
493
+ )
494
+
488
495
# Populate key sizes
489
496
theme $ legend.key.width <- calc_element(" legend.key.width" , theme )
490
497
theme $ legend.key.height <- calc_element(" legend.key.height" , theme )
491
498
492
- grobs <- self $ draw(theme , default_position , theme $ legend.direction )
499
+ grobs <- self $ draw(theme , positions , theme $ legend.direction )
500
+ keep <- ! vapply(grobs , is.zero , logical (1 ), USE.NAMES = FALSE )
501
+ grobs <- grobs [keep ]
493
502
if (length(grobs ) < 1 ) {
494
503
return (zeroGrob())
495
504
}
496
- grobs <- grobs [order(names(grobs ))]
505
+
506
+ # prepare the position of inside legends
507
+ default_inside_just <- calc_element(" legend.justification.inside" , theme )
508
+ default_inside_position <- calc_element(" legend.position.inside" , theme )
509
+
510
+ groups <- data_frame0(
511
+ positions = positions ,
512
+ justs = list (NULL ),
513
+ coords = list (NULL )
514
+ )
515
+
516
+ # we grouped the legends by the positions, for inside legends, they'll be
517
+ # splitted by the actual inside coordinate
518
+ for (i in which(positions == " inside" )) {
519
+ # the actual inside position and justification can be set in each guide
520
+ # by `theme` argument, here, we won't use `calc_element()` which will
521
+ # use inherits from `legend.justification` or `legend.position`, we only
522
+ # follow the inside elements from the guide theme
523
+ just <- params [[i ]]$ theme [[" legend.justification.inside" ]]
524
+ just <- valid.just(just %|| % default_inside_just )
525
+ coord <- params [[i ]]$ theme [[" legend.position.inside" ]]
526
+ coord <- coord %|| % default_inside_position %|| % just
527
+
528
+ groups $ justs [[i ]] <- just
529
+ groups $ coord [[i ]] <- coord
530
+ }
531
+
532
+ groups <- vec_group_loc(vec_slice(groups , keep ))
533
+ grobs <- vec_chop(grobs , indices = groups $ loc )
534
+ names(grobs ) <- groups $ key $ positions
497
535
498
536
# Set spacing
499
537
theme $ legend.spacing <- theme $ legend.spacing %|| % unit(0.5 , " lines" )
500
538
theme $ legend.spacing.y <- calc_element(" legend.spacing.y" , theme )
501
539
theme $ legend.spacing.x <- calc_element(" legend.spacing.x" , theme )
502
540
503
- Map(
504
- grobs = grobs ,
505
- position = names(grobs ),
506
- self $ package_box ,
507
- MoreArgs = list (theme = theme )
508
- )
541
+ # prepare output
542
+ for (i in vec_seq_along(groups )) {
543
+ adjust <- NULL
544
+ position <- groups $ key $ position [i ]
545
+ if (position == " inside" ) {
546
+ adjust <- theme(
547
+ legend.position.inside = groups $ key $ coord [[i ]],
548
+ legend.justification.inside = groups $ key $ justs [[i ]]
549
+ )
550
+ }
551
+ grobs [[i ]] <- self $ package_box(grobs [[i ]], position , theme + adjust )
552
+ }
553
+
554
+ # merge inside grobs into single gtable
555
+ is_inside <- names(grobs ) == " inside"
556
+ if (sum(is_inside ) > 1 ) {
557
+ inside <- gtable(unit(1 , " npc" ), unit(1 , " npc" ))
558
+ inside <- gtable_add_grob(
559
+ inside , grobs [is_inside ],
560
+ t = 1 , l = 1 , clip = " off" ,
561
+ name = paste0(" guide-box-inside-" , seq_len(sum(is_inside )))
562
+ )
563
+ grobs <- grobs [! is_inside ]
564
+ grobs $ inside <- inside
565
+ }
566
+
567
+ # fill in missing guides
568
+ grobs [setdiff(c(.trbl , " inside" ), names(grobs ))] <- list (zeroGrob())
569
+
570
+ grobs
509
571
},
510
572
511
573
# Render the guides into grobs
512
- draw = function (self , theme ,
513
- default_position = " right" ,
514
- direction = NULL ,
574
+ draw = function (self , theme , positions , direction = NULL ,
515
575
params = self $ params ,
516
576
guides = self $ guides ) {
517
- positions <- vapply(
518
- params ,
519
- function (p ) p $ position [1 ] %|| % default_position ,
520
- character (1 )
521
- )
522
- positions <- factor (positions , levels = c(.trbl , " inside" ))
523
-
524
577
directions <- rep(direction %|| % " vertical" , length(positions ))
525
578
if (is.null(direction )) {
526
579
directions [positions %in% c(" top" , " bottom" )] <- " horizontal"
@@ -529,14 +582,16 @@ Guides <- ggproto(
529
582
grobs <- vector(" list" , length(guides ))
530
583
for (i in seq_along(grobs )) {
531
584
grobs [[i ]] <- guides [[i ]]$ draw(
532
- theme = theme , position = as.character( positions [i ]) ,
585
+ theme = theme , position = positions [i ],
533
586
direction = directions [i ], params = params [[i ]]
534
587
)
535
588
}
536
- keep <- ! vapply(grobs , is.zero , logical (1 ))
537
- split(grobs [keep ], positions [keep ])
589
+ grobs
538
590
},
539
591
592
+ # here, we put `inside_position` and `inside_just` in the last, so that it
593
+ # won't break current implement of patchwork, which depends on the top three
594
+ # arguments to collect guides
540
595
package_box = function (grobs , position , theme ) {
541
596
542
597
if (is.zero(grobs ) || length(grobs ) == 0 ) {
@@ -699,7 +754,6 @@ Guides <- ggproto(
699
754
guides $ name <- " guide-box"
700
755
guides
701
756
},
702
-
703
757
# # Utilities -----------------------------------------------------------------
704
758
705
759
print = function (self ) {
0 commit comments