Skip to content

Commit 62463c0

Browse files
Fix interactive resizing regression from Issue #536 (#541)
1 parent 3130e7b commit 62463c0

1 file changed

Lines changed: 89 additions & 42 deletions

File tree

R/legend.R

Lines changed: 89 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,85 @@ measure_legend_inset = function(legend_env) {
205205
}
206206

207207

208+
# Internal workhorse function for legend rendering
209+
# This function is called inside recordGraphics() so that all coordinate-dependent
210+
# calculations are re-executed when the plot window is resized
211+
tinylegend = function(legend_env) {
212+
# Reset to base values (before soma/inset were applied)
213+
# This is necessary because legend_env is passed by reference and modifications
214+
# persist across recordGraphics() replays
215+
legend_env$omar = legend_env$omar_base
216+
legend_env$ooma = legend_env$ooma_base
217+
legend_env$args[["inset"]] = legend_env$inset_base
218+
219+
# Re-measure legend dimensions (device size may have changed on resize)
220+
legend_env$dims = measure_fake_legend(legend_env)
221+
222+
# Calculate and apply soma (outer margin adjustment based on legend size)
223+
soma = if (legend_env$outer_side) {
224+
grconvertX(legend_env$dims$rect$w, to = "lines") - grconvertX(0, to = "lines")
225+
} else if (legend_env$outer_end) {
226+
grconvertY(legend_env$dims$rect$h, to = "lines") - grconvertY(0, to = "lines")
227+
} else {
228+
0
229+
}
230+
soma = soma + sum(legend_env$lmar)
231+
232+
if (legend_env$outer_side) {
233+
legend_env$ooma[if (legend_env$outer_right) 4 else 2] = soma
234+
} else if (legend_env$outer_end) {
235+
if (legend_env$outer_bottom) {
236+
legend_env$ooma[1] = soma
237+
} else {
238+
legend_env$omar[3] = legend_env$omar[3] + soma - legend_env$topmar_epsilon
239+
par(mar = legend_env$omar)
240+
}
241+
}
242+
par(oma = legend_env$ooma)
243+
244+
# Calculate inset for legend positioning
245+
legend_env$inset = measure_legend_inset(legend_env)
246+
247+
# Refresh plot area for exact inset spacing
248+
# (Uses hook to preserve existing plot with par(new = TRUE))
249+
oldhook = getHook("before.plot.new")
250+
setHook("before.plot.new", function() par(new = TRUE), action = "append")
251+
setHook("before.plot.new", function() par(mar = legend_env$omar), action = "append")
252+
plot.new()
253+
setHook("before.plot.new", oldhook, action = "replace")
254+
255+
# Set the inset in legend args
256+
legend_env$args[["inset"]] = if (legend_env$user_inset) {
257+
legend_env$args[["inset"]] + legend_env$inset
258+
} else {
259+
legend_env$inset
260+
}
261+
262+
# Draw the legend
263+
if (legend_env$gradient) {
264+
# Ensure col is set correctly for gradients
265+
if (!more_than_n_unique(legend_env$args[["col"]], 1)) {
266+
if (!is.null(legend_env$args[["pt.bg"]]) && length(legend_env$args[["pt.bg"]]) == 100) {
267+
legend_env$args[["col"]] = legend_env$args[["pt.bg"]]
268+
}
269+
}
270+
271+
draw_gradient_swatch(
272+
legend_args = legend_env$args,
273+
fklgnd = legend_env$dims,
274+
lmar = legend_env$lmar,
275+
outer_side = legend_env$outer_side,
276+
outer_end = legend_env$outer_end,
277+
outer_right = legend_env$outer_right,
278+
outer_bottom = legend_env$outer_bottom,
279+
user_inset = legend_env$user_inset
280+
)
281+
} else {
282+
do.call("legend", legend_env$args)
283+
}
284+
}
285+
286+
208287
# Measure legend dimensions using a fake (non-plotted) legend
209288
measure_fake_legend = function(legend_env) {
210289
fklgnd.args = modifyList(
@@ -806,55 +885,23 @@ draw_legend = function(
806885
new_plot = new_plot
807886
)
808887

809-
# Adjust margins for outer placement, measure, and optionally apply
810-
legend_outer_margins(legend_env, apply = draw)
888+
# Initial setup: adjust margins, call plot.new, and measure (but don't apply soma yet)
889+
legend_outer_margins(legend_env, apply = FALSE)
811890

812891
if (!draw) {
813892
return(legend_env$dims)
814893
}
815894

816-
# Calculate inset
817-
legend_env$inset = measure_legend_inset(legend_env)
895+
# Store base values AFTER legend_outer_margins setup (before soma/inset are applied)
896+
# These are needed so tinylegend() can reset to them on each recordGraphics replay
897+
legend_env$omar_base = legend_env$omar
898+
legend_env$ooma_base = legend_env$ooma
899+
legend_env$inset_base = legend_env$args[["inset"]]
818900

819-
# Refresh plot area for exact inset spacing
820-
oldhook = getHook("before.plot.new")
821-
setHook("before.plot.new", function() par(new = TRUE), action = "append")
822-
setHook("before.plot.new", function() par(mar = legend_env$omar), action = "append")
823-
plot.new()
824-
setHook("before.plot.new", oldhook, action = "replace")
825-
826-
# Set the inset in args
827-
legend_env$args[["inset"]] = if (legend_env$user_inset) {
828-
legend_env$args[["inset"]] + legend_env$inset
829-
} else {
830-
legend_env$inset
831-
}
832-
833-
# Draw wrapped in recordGraphics() to preserve spacing if plot is resized
901+
# Wrap margin application, inset calculation, and drawing in recordGraphics()
902+
# so everything recalculates correctly when the plot window is resized
834903
recordGraphics(
835-
{
836-
if (legend_env$gradient) {
837-
# Ensure col is set correctly for gradients
838-
if (!more_than_n_unique(legend_env$args[["col"]], 1)) {
839-
if (!is.null(legend_env$args[["pt.bg"]]) && length(legend_env$args[["pt.bg"]]) == 100) {
840-
legend_env$args[["col"]] = legend_env$args[["pt.bg"]]
841-
}
842-
}
843-
844-
draw_gradient_swatch(
845-
legend_args = legend_env$args,
846-
fklgnd = legend_env$dims,
847-
lmar = legend_env$lmar,
848-
outer_side = legend_env$outer_side,
849-
outer_end = legend_env$outer_end,
850-
outer_right = legend_env$outer_right,
851-
outer_bottom = legend_env$outer_bottom,
852-
user_inset = legend_env$user_inset
853-
)
854-
} else {
855-
do.call("legend", legend_env$args)
856-
}
857-
},
904+
tinylegend(legend_env),
858905
list = list(legend_env = legend_env),
859906
env = getNamespace("tinyplot")
860907
)

0 commit comments

Comments
 (0)