@@ -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
209288measure_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