@@ -946,11 +946,22 @@ public List<BvvMultiSource> show(final AbstractSpimData<?> spimData) {
946946 * @return list containing a single {@link BvvMultiSource} grouping all setups
947947 */
948948 public List <BvvMultiSource > show (final SpimDataUtils .N5Sources n5Sources ) {
949- final BvvOptions opts = bvvHandle != null
950- ? bvv .vistools .Bvv .options ().addTo (bvvHandle ) : options ;
949+ // Whether this Bvv instance had no window at all before this call, distinct from bvvHandle's state
950+ // *during* the loop below, which changes as soon as the first channel is shown. Needed so "attach
951+ // control panel" further down still fires exactly once/
952+ final boolean firstEverSource = bvvHandle == null ;
951953 final List <BvvStackSource <?>> sources = new ArrayList <>();
952- for (final SourceAndConverter <?> soc : n5Sources .sources )
953- sources .add (BvvFunctions .show (soc , n5Sources .numTimepoints , opts ));
954+ for (final SourceAndConverter <?> soc : n5Sources .sources ) {
955+ // NB: Each channel is a separate BvvFunctions.show(...) call, and options *without* addTo(...) always
956+ // opens a brand new top-level window. Deciding "attach to existing handle" once, before any
957+ // channel exists, meant bvvHandle was still null for the whole loop on a fresh Bvv, so
958+ // every channel got its own window instead of being appended to the first one
959+ final BvvOptions opts = bvvHandle != null
960+ ? bvv .vistools .Bvv .options ().addTo (bvvHandle ) : options ;
961+ final BvvStackSource <?> source = BvvFunctions .show (soc , n5Sources .numTimepoints , opts );
962+ sources .add (source );
963+ if (bvvHandle == null ) bvvHandle = source .getBvvHandle (); // subsequent channels attach to this window
964+ }
954965 if (sources .isEmpty ()) return Collections .emptyList ();
955966
956967 // Populate dims/cal from the first source's own metadata; there is no
@@ -979,9 +990,10 @@ public List<BvvMultiSource> show(final SpimDataUtils.N5Sources n5Sources) {
979990 if (datasetName == null || datasetName .isBlank ())
980991 datasetName = "Dataset " + (multiSources .size () + 1 );
981992
982- // Attach control panel on first source of the first dataset
983- if (bvvHandle == null ) {
984- bvvHandle = sources .getFirst ().getBvvHandle ();
993+ // Attach control panel on first source of the first dataset. bvvHandle is already set by
994+ // the loop above, so this checks firstEverSource instead to still fire exactly once, the
995+ // very first time any source is shown
996+ if (firstEverSource ) {
985997 attachControlPanel (sources .getFirst ());
986998 }
987999
@@ -1626,12 +1638,12 @@ private JComponent sntToolbar(final BvvActions actions) {
16261638 final ButtonGroup bg1 = GuiUtils .Buttons .noneSelectedButtonGroup ();
16271639 final JToggleButton b1 = GuiUtils .Buttons .toolbarToggleButton (tracer .getToggleAction (true ),
16281640 "Start/stop manual tracing" , IconFactory .GLYPH .PEN , IconFactory .GLYPH .PEN );
1629- final JToggleButton b2 = GuiUtils .Buttons .toolbarToggleButton (tracer .getToggleAction (false ),
1630- "Start/stop interactive tracing" , IconFactory .GLYPH .ROUTE , IconFactory .GLYPH .ROUTE );
1631- bg1 .add (b1 );
1632- bg1 .add (b2 );
1633- toolbar .add (b1 );
1634- toolbar .add (b2 );
1641+ final JToggleButton b2 = GuiUtils .Buttons .toolbarToggleButton (tracer .getToggleAction (false ),
1642+ "Start/stop interactive tracing" , IconFactory .GLYPH .ROUTE , IconFactory .GLYPH .ROUTE );
1643+ bg1 .add (b1 );
1644+ bg1 .add (b2 );
1645+ toolbar .add (b1 );
1646+ toolbar .add (b2 );
16351647 toolbar .addSeparator ();
16361648 toolbar .add (Box .createHorizontalGlue ());
16371649 toolbar .addSeparator ();
0 commit comments