diff --git a/src/main/java/bdv/tools/HelpDialog.java b/src/main/java/bdv/tools/HelpDialog.java
index e056c40bd56ef990baa1a3c6224ae8005168f59a..61edcb7ed41fdb417c49f216def1d1f7fa9b5a6b 100644
--- a/src/main/java/bdv/tools/HelpDialog.java
+++ b/src/main/java/bdv/tools/HelpDialog.java
@@ -28,6 +28,8 @@
  */
 package bdv.tools;
 
+import bdv.util.DelayedPackDialog;
+
 import java.awt.BorderLayout;
 import java.awt.Dimension;
 import java.awt.Frame;
@@ -42,14 +44,12 @@ import javax.swing.ActionMap;
 import javax.swing.BorderFactory;
 import javax.swing.InputMap;
 import javax.swing.JComponent;
-import javax.swing.JDialog;
 import javax.swing.JEditorPane;
 import javax.swing.JScrollPane;
 import javax.swing.KeyStroke;
 import javax.swing.ScrollPaneConstants;
-import javax.swing.WindowConstants;
 
-public class HelpDialog extends JDialog
+public class HelpDialog extends DelayedPackDialog
 {
 	private static final long serialVersionUID = 1L;
 
@@ -100,7 +100,6 @@ public class HelpDialog extends JDialog
 			am.put( hideKey, hideAction );
 
 			pack();
-			setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
 		}
 		catch ( final IOException e )
 		{
diff --git a/src/main/java/bdv/tools/RecordMaxProjectionDialog.java b/src/main/java/bdv/tools/RecordMaxProjectionDialog.java
index d2b076638596892417775eb2879c09fcd123ee0e..3ec543035640a7494229a28b3e862ba33cc40d46 100644
--- a/src/main/java/bdv/tools/RecordMaxProjectionDialog.java
+++ b/src/main/java/bdv/tools/RecordMaxProjectionDialog.java
@@ -30,6 +30,7 @@ package bdv.tools;
 
 import bdv.cache.CacheControl;
 import bdv.export.ProgressWriter;
+import bdv.util.DelayedPackDialog;
 import bdv.util.Prefs;
 import bdv.viewer.BasicViewerState;
 import bdv.viewer.ViewerPanel;
@@ -56,7 +57,6 @@ import javax.swing.BoxLayout;
 import javax.swing.InputMap;
 import javax.swing.JButton;
 import javax.swing.JComponent;
-import javax.swing.JDialog;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -64,7 +64,6 @@ import javax.swing.JSpinner;
 import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.SpinnerNumberModel;
-import javax.swing.WindowConstants;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import net.imglib2.Cursor;
@@ -77,7 +76,7 @@ import bdv.viewer.OverlayRenderer;
 import bdv.viewer.render.RenderTarget;
 import net.imglib2.util.LinAlgHelpers;
 
-public class RecordMaxProjectionDialog extends JDialog implements OverlayRenderer
+public class RecordMaxProjectionDialog extends DelayedPackDialog implements OverlayRenderer
 {
 	private static final long serialVersionUID = 1L;
 
@@ -275,7 +274,6 @@ public class RecordMaxProjectionDialog extends JDialog implements OverlayRendere
 		am.put( hideKey, hideAction );
 
 		pack();
-		setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
 	}
 
 	/**
diff --git a/src/main/java/bdv/tools/RecordMovieDialog.java b/src/main/java/bdv/tools/RecordMovieDialog.java
index 9ae851aa1bcd269c953521ebcd1ff63f0987ca5c..cf35bcbd141638108ea1cde6d74bec026dd63084 100644
--- a/src/main/java/bdv/tools/RecordMovieDialog.java
+++ b/src/main/java/bdv/tools/RecordMovieDialog.java
@@ -30,6 +30,7 @@ package bdv.tools;
 
 import bdv.cache.CacheControl;
 import bdv.export.ProgressWriter;
+import bdv.util.DelayedPackDialog;
 import bdv.util.Prefs;
 import bdv.viewer.BasicViewerState;
 import bdv.viewer.ViewerPanel;
@@ -55,7 +56,6 @@ import javax.swing.BoxLayout;
 import javax.swing.InputMap;
 import javax.swing.JButton;
 import javax.swing.JComponent;
-import javax.swing.JDialog;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -63,14 +63,13 @@ import javax.swing.JSpinner;
 import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.SpinnerNumberModel;
-import javax.swing.WindowConstants;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import net.imglib2.realtransform.AffineTransform3D;
 import bdv.viewer.OverlayRenderer;
 import bdv.viewer.render.RenderTarget;
 
-public class RecordMovieDialog extends JDialog implements OverlayRenderer
+public class RecordMovieDialog extends DelayedPackDialog implements OverlayRenderer
 {
 	private static final long serialVersionUID = 1L;
 
@@ -248,7 +247,6 @@ public class RecordMovieDialog extends JDialog implements OverlayRenderer
 		am.put( hideKey, hideAction );
 
 		pack();
-		setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
 	}
 
 	public void recordMovie( final int width, final int height, final int minTimepointIndex, final int maxTimepointIndex, final File dir ) throws IOException
diff --git a/src/main/java/bdv/tools/VisibilityAndGroupingDialog.java b/src/main/java/bdv/tools/VisibilityAndGroupingDialog.java
index b4a42dca5980fff34b6b93abacda8e8eb0ad09f1..b1da27e26f79a5bd9cfe80c2da58c2047b44cbee 100644
--- a/src/main/java/bdv/tools/VisibilityAndGroupingDialog.java
+++ b/src/main/java/bdv/tools/VisibilityAndGroupingDialog.java
@@ -28,6 +28,7 @@
  */
 package bdv.tools;
 
+import bdv.util.DelayedPackDialog;
 import bdv.viewer.SourceAndConverter;
 import bdv.viewer.ViewerState;
 import bdv.viewer.VisibilityAndGrouping;
@@ -61,19 +62,17 @@ import javax.swing.ButtonGroup;
 import javax.swing.InputMap;
 import javax.swing.JCheckBox;
 import javax.swing.JComponent;
-import javax.swing.JDialog;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
 import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.SwingUtilities;
-import javax.swing.WindowConstants;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 
 @Deprecated
-public class VisibilityAndGroupingDialog extends JDialog
+public class VisibilityAndGroupingDialog extends DelayedPackDialog
 {
 	private static final long serialVersionUID = 1L;
 
@@ -117,7 +116,7 @@ public class VisibilityAndGroupingDialog extends JDialog
 		content.add( visibilityPanel );
 		content.add( groupingPanel );
 		content.add( modePanel );
-		getContentPane().add( content, BorderLayout.NORTH );
+		add( content, BorderLayout.NORTH );
 
 		final ActionMap am = getRootPane().getActionMap();
 		final InputMap im = getRootPane().getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
@@ -147,7 +146,6 @@ public class VisibilityAndGroupingDialog extends JDialog
 		} );
 
 		pack();
-		setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
 	}
 
 	public void update()
diff --git a/src/main/java/bdv/tools/brightness/BrightnessDialog.java b/src/main/java/bdv/tools/brightness/BrightnessDialog.java
index 1e71f960cdaec7ba438f14de6c7fe35903ae15c1..ce98337f48a7fe79259fc397cb4b68e5892fb93e 100644
--- a/src/main/java/bdv/tools/brightness/BrightnessDialog.java
+++ b/src/main/java/bdv/tools/brightness/BrightnessDialog.java
@@ -60,11 +60,11 @@ import javax.swing.JSpinner;
 import javax.swing.KeyStroke;
 import javax.swing.SpinnerNumberModel;
 import javax.swing.SwingUtilities;
-import javax.swing.WindowConstants;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 
 import bdv.util.InvokeOnEDT;
+import bdv.util.DelayedPackDialog;
 import mpicbg.spim.data.generic.sequence.BasicViewSetup;
 import net.imglib2.type.numeric.ARGBType;
 
@@ -75,7 +75,7 @@ import net.imglib2.type.numeric.ARGBType;
  * @author Tobias Pietzsch <tobias.pietzsch@gmail.com>
  */
 @Deprecated
-public class BrightnessDialog extends JDialog
+public class BrightnessDialog extends DelayedPackDialog
 {
 	public BrightnessDialog( final Frame owner, final SetupAssignments setupAssignments )
 	{
@@ -148,7 +148,6 @@ public class BrightnessDialog extends JDialog
 		} );
 
 		pack();
-		setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
 	}
 
 	public static class ColorsPanel extends JPanel
diff --git a/src/main/java/bdv/ui/BdvDefaultCards.java b/src/main/java/bdv/ui/BdvDefaultCards.java
index f0d1ab1c6b13201c9440b48b0e3629f08a04b5d4..403dd1fcf45d9d2751da5347a4c0a3332ca04301 100644
--- a/src/main/java/bdv/ui/BdvDefaultCards.java
+++ b/src/main/java/bdv/ui/BdvDefaultCards.java
@@ -42,6 +42,7 @@ import java.awt.Insets;
 import java.awt.KeyboardFocusManager;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.lang.ref.WeakReference;
 import javax.swing.JComponent;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
@@ -85,7 +86,7 @@ public class BdvDefaultCards
 		tablePanel.setPreferredSize( new Dimension( 300, 245 ) );
 
 		// -- Groups tree --
-		SourceGroupTree tree = new SourceGroupTree( state, viewer.getOptionValues().getInputTriggerConfig() );
+		final SourceGroupTree tree = new SourceGroupTree( state, viewer.getOptionValues().getInputTriggerConfig() );
 //		tree.setPreferredSize( new Dimension( 300, 200 ) );
 		tree.setVisibleRowCount( 10 );
 		tree.setEditable( true );
@@ -103,65 +104,95 @@ public class BdvDefaultCards
 		treePanel.add( editPanelTree, BorderLayout.SOUTH );
 		treePanel.setPreferredSize( new Dimension( 300, 225 ) );
 
-		// -- handle focus --
-		KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener( "focusOwner", new PropertyChangeListener()
+		new FocusListener( tablePanel, table, treePanel, tree );
+
+		cards.addCard( DEFAULT_VIEWERMODES_CARD, "Display Modes", new DisplaySettingsPanel( viewer.state() ), true, new Insets( 0, 4, 4, 0 ) );
+		cards.addCard( DEFAULT_SOURCES_CARD, "Sources", tablePanel, true, new Insets( 0, 4, 0, 0 ) );
+		cards.addCard( DEFAULT_SOURCEGROUPS_CARD, "Groups", treePanel, true, new Insets( 0, 4, 0, 0 ) );
+	}
+
+	private static class FocusListener implements PropertyChangeListener
+	{
+		private final KeyboardFocusManager keyboardFocusManager;
+
+		private final WeakReference< JPanel > tablePanel;
+		private final WeakReference< SourceTable > table;
+		private final WeakReference< JPanel > treePanel;
+		private final WeakReference< SourceGroupTree > tree;
+
+		static final int MAX_DEPTH = 8;
+		boolean tableFocused;
+		boolean treeFocused;
+
+		FocusListener( final JPanel tablePanel, final SourceTable table, final JPanel treePanel, final SourceGroupTree tree )
 		{
-			static final int MAX_DEPTH = 8;
-			boolean tableFocused;
-			boolean treeFocused;
+			this.tablePanel = new WeakReference<>( tablePanel );
+			this.table = new WeakReference<>( table );
+			this.treePanel = new WeakReference<>( treePanel );
+			this.tree = new WeakReference<>( tree );
+
+			keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+			keyboardFocusManager.addPropertyChangeListener( "focusOwner", this );
+		}
 
-			void focusTable( boolean focus )
+		void focusTable( final boolean focus )
+		{
+			if ( focus != tableFocused )
 			{
-				if ( focus != tableFocused )
-				{
-					tableFocused = focus;
+				tableFocused = focus;
+				final SourceTable table = this.table.get();
+				if ( table != null )
 					table.setSelectionBackground( focus );
-				}
 			}
+		}
 
-			void focusTree( boolean focus )
+		void focusTree( final boolean focus )
+		{
+			if ( focus != treeFocused )
 			{
-				if ( focus != treeFocused )
-				{
-					treeFocused = focus;
+				treeFocused = focus;
+				final SourceGroupTree tree = this.tree.get();
+				if ( tree != null )
 					tree.setSelectionBackground( focus );
-				}
+			}
+		}
+
+		@Override
+		public void propertyChange( final PropertyChangeEvent evt )
+		{
+			final JPanel tablePanel = this.tablePanel.get();
+			final JPanel treePanel = this.treePanel.get();
+			if ( tablePanel == null && treePanel == null )
+			{
+				keyboardFocusManager.removePropertyChangeListener( "focusOwner", this );
+				return;
 			}
 
-			@Override
-			public void propertyChange( final PropertyChangeEvent evt )
+			if ( evt.getNewValue() instanceof JComponent )
 			{
-				if ( evt.getNewValue() instanceof JComponent )
+				final JComponent component = ( JComponent ) evt.getNewValue();
+				for ( int i = 0; i < MAX_DEPTH; ++i )
 				{
-					JComponent component = ( JComponent ) evt.getNewValue();
-					for ( int i = 0; i < MAX_DEPTH; ++i )
+					final Container parent = component.getParent();
+					if ( !( parent instanceof JComponent ) )
+						break;
+
+					if ( component == treePanel )
+					{
+						focusTable( false );
+						focusTree( true );
+						return;
+					}
+					else if ( component == tablePanel )
 					{
-						Container parent = component.getParent();
-						if ( ! ( parent instanceof JComponent ) )
-							break;
-
-						component = ( JComponent ) parent;
-						if ( component == treePanel )
-						{
-							focusTable( false );
-							focusTree( true );
-							return;
-						}
-						else if ( component == tablePanel )
-						{
-							focusTable( true );
-							focusTree( false );
-							return;
-						}
+						focusTable( true );
+						focusTree( false );
+						return;
 					}
-					focusTable( false );
-					focusTree( false );
 				}
+				focusTable( false );
+				focusTree( false );
 			}
-		} );
-
-		cards.addCard( DEFAULT_VIEWERMODES_CARD, "Display Modes", new DisplaySettingsPanel( viewer.state() ), true, new Insets( 0, 4, 4, 0 ) );
-		cards.addCard( DEFAULT_SOURCES_CARD, "Sources", tablePanel, true, new Insets( 0, 4, 0, 0 ) );
-		cards.addCard( DEFAULT_SOURCEGROUPS_CARD, "Groups", treePanel, true, new Insets( 0, 4, 0, 0 ) );
+		}
 	}
 }
diff --git a/src/main/java/bdv/util/DelayedPackDialog.java b/src/main/java/bdv/util/DelayedPackDialog.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d3d178758cbb8635f8df5488f2e473366da556a
--- /dev/null
+++ b/src/main/java/bdv/util/DelayedPackDialog.java
@@ -0,0 +1,40 @@
+package bdv.util;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * A {@code JDialog} that delays {@code pack()} calls until the dialog is made visible.
+ */
+public class DelayedPackDialog extends JDialog
+{
+	private volatile boolean packIsPending = false;
+
+	public DelayedPackDialog( Frame owner, String title, boolean modal )
+	{
+		super( owner, title, modal );
+	}
+
+	@Override
+	public void pack()
+	{
+		if ( isVisible() )
+		{
+			packIsPending = false;
+			super.pack();
+		}
+		else
+			packIsPending = true;
+	}
+
+	@Override
+	public void setVisible( boolean visible )
+	{
+		if ( visible && packIsPending )
+		{
+			packIsPending = false;
+			super.pack();
+		}
+		super.setVisible( visible );
+	}
+}
diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java
index 612592832ad6f0b45712c57da4e6b5b673e28cbb..39bc9a54847fe633dd6451fd708e4ffc58bda556 100644
--- a/src/main/java/bdv/viewer/ViewerPanel.java
+++ b/src/main/java/bdv/viewer/ViewerPanel.java
@@ -260,7 +260,7 @@ public class ViewerPanel extends JPanel implements OverlayRenderer, PainterThrea
 
 		renderingExecutorService = Executors.newFixedThreadPool(
 				options.getNumRenderingThreads(),
-				new RenderThreadFactory() );
+				new RenderThreadFactory( threadGroup ) );
 		imageRenderer = new MultiResolutionRenderer(
 				renderTarget, painterThread,
 				options.getScreenScales(),
@@ -1153,14 +1153,21 @@ public class ViewerPanel extends JPanel implements OverlayRenderer, PainterThrea
 
 	protected static final AtomicInteger panelNumber = new AtomicInteger( 1 );
 
-	protected class RenderThreadFactory implements ThreadFactory
+	protected static class RenderThreadFactory implements ThreadFactory
 	{
+		private final ThreadGroup threadGroup;
+
 		private final String threadNameFormat = String.format(
 				"bdv-panel-%d-thread-%%d",
 				panelNumber.getAndIncrement() );
 
 		private final AtomicInteger threadNumber = new AtomicInteger( 1 );
 
+		protected RenderThreadFactory( final ThreadGroup threadGroup )
+		{
+			this.threadGroup = threadGroup;
+		}
+
 		@Override
 		public Thread newThread( final Runnable r )
 		{