Focus lost closes JComboBox dropdown immediately when stacked dockable externalized

In this scenario I have multiple DefaultSingleCDockables, each containing a JComboBox, added to the same location so that they are stacked. I pull one of them off and leave the other in place so that I can view both at the same time. Click on the one that was not detached so that it has focus. Now, click on the down arrow of the externalized dockable’s combobox. What I’m seeing is that the JComboBox gains focus, the dropdown is shown, and it immediately loses the focus and the dropdown closes. Several focus exchanges fire after the combobox popup is shown. See below SCCE that demonstrates this behavior. I ran into this in Java 6 and 7, DockingFrames Preview Version 1.1.2 17b.

import bibliothek.gui.dock.StackDockStation;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.group.TopMostGroupBehavior;
import bibliothek.gui.dock.common.grouping.PlaceholderGrouping;
import bibliothek.gui.dock.common.perspective.CGridPerspective;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.station.stack.tab.layouting.TabPlacement;

import bibliothek.util.Path;

import java.awt.BorderLayout;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test
  extends JFrame
{
  public Test()
  {
    super();
    CControl control = new CControl(this);

    add(control.getContentArea(), BorderLayout.CENTER);
    control.setGroupBehavior(new TopMostGroupBehavior());
    control.putProperty(StackDockStation.TAB_PLACEMENT,
                        TabPlacement.TOP_OF_DOCKABLE);

    CPerspective layout =
      control.getPerspectives().createEmptyPerspective();
    CGridPerspective center = layout.getContentArea().getCenter();
    Path path = new Path("custom", "flightlist");

    center.gridPlaceholder(0, 0, 1, 1, path);
    control.getPerspectives().setPerspective(layout, true);
    PlaceholderGrouping group = new PlaceholderGrouping(control, path);


    JPanel comboPanel1 = new JPanel();
    comboPanel1.add(new JComboBox(new String[]
      {
        "OPTION1", "OPTION2"
      }));

    DefaultSingleCDockable dockable =
      new DefaultSingleCDockable("1", null, "1", comboPanel1, null);

    CLocation location;
    if (control.getCDockableCount() > 0)
    {
      location =
        control.getCDockable(control.getCDockableCount() -
                             1).getBaseLocation();
    }
    else
    {
      location = control.getDefaultLocation();
    }

    dockable.setCloseable(false);
    dockable.setMaximizable(false);
    dockable.setMinimizable(false);
    dockable.setTitleShown(false);
    dockable.setGrouping(group);
    dockable.setSingleTabShown(true);
    dockable.setLocation(location);
    control.addDockable(dockable);


    dockable.setVisible(true);


    JPanel comboPanel2 = new JPanel();
    comboPanel2.add(new JComboBox(new String[]
      {
        "OPTION1", "OPTION2"
      }));


    DefaultSingleCDockable dockable2 =
      new DefaultSingleCDockable("2", null, "2", comboPanel2, null);

    CLocation location2;
    if (control.getCDockableCount() > 0)
    {
      location2 =
        control.getCDockable(control.getCDockableCount() -
                             1).getBaseLocation();
    }
    else
    {
      location2 = control.getDefaultLocation();
    }

    dockable2.setCloseable(false);
    dockable2.setMaximizable(false);
    dockable2.setMinimizable(false);
    dockable2.setTitleShown(false);
    dockable2.setGrouping(group);
    dockable2.setSingleTabShown(true);
    dockable2.setLocation(location2);
    control.addDockable(dockable2);


    dockable2.setVisible(true);
    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        pack();
        setVisible(true);
      }
    });
  }
  
  public static void main(String[] args)
  {
    new Test();
  }
}```

I’ll try to reproduce the issue, on what OS did you run the application? (Windows, Linux, … ?).

*** Edit ***

I tried your application on my Kubutuntu, and when one JComboBox was open clicking on the other did not open it. I had to click twice (I admit, this is rather annoying behavior).

Then I wrote an application without DockingFrames (see below) and could observe the exact same behavior.

So right now I am not a 100% convinced that this is an issue of DockingFrames, it could be just “normal” behavior of Swing. Please try out the application below, and tell me how it behaves compare to your example.


import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test2 extends JFrame{
	public Test2() {
		super();

		JPanel comboPanel1 = new JPanel();
		comboPanel1.add( new JComboBox( new String[]{
				"OPTION1", "OPTION2"
		} ) );
		
		add( comboPanel1 );
		pack();
		

setVisible( true );

		JDialog dialog = new JDialog( this, "Other", false );
		JPanel comboPanel2 = new JPanel();
		comboPanel2.add( new JComboBox( new String[]{
				"OPTION1", "OPTION2"
		} ) );
		dialog.add( comboPanel2 );
		dialog.pack();
		dialog.setLocation( getX() + getWidth(), getY() );
		dialog.setVisible( true );
	}

	public static void main( String[] args ) {
		new Test2();
	}
}

On some old Windows (XP, yeah) the Swing example works: I can click each Arrow-Buttons, alternatingly, and it properly shows the respective Combo Menu. (I tried it with different L&Fs, because I thought that it might be related to that, but it worked with all of them).

Beni, thanks for the quick reply. I’m running on linux, RHEL 6.4.

I just ran your example and it seems like in vanilla swing, if the combobox dropdown is open on the first window and I click on the down arrow of the other you’re correct, it takes 2 clicks. As a result, I would not attribute that specific case to DockingFrames. But I see different behavior if the dropdown is not open on the combo losing focus.

I ran my example without having the first combobox dropdown open. What I’m seeing is if dockable1 has focus at all, even if the JComboBox within it does not have the focus, and the combobox arrow on dockable2 is clicked the dropdown for comboxbox2 is shown and immediately hidden, requiring another click. This is not the case with the vanilla Swing example in my environment.

If you look here, I’ve used the same example as before just removed the JComboBox from the first dockable completely. Problem still exists if dockable 1 has focus when the opposite combobox arrow is clicked:

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.group.TopMostGroupBehavior;
import bibliothek.gui.dock.common.grouping.PlaceholderGrouping;
import bibliothek.gui.dock.common.perspective.CGridPerspective;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.station.stack.tab.layouting.TabPlacement;

import bibliothek.util.Path;

import java.awt.BorderLayout;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test
  extends JFrame
{
  public Test()
  {
    super();
    CControl control = new CControl(this);

    add(control.getContentArea(), BorderLayout.CENTER);
    control.setGroupBehavior(new TopMostGroupBehavior());
    control.putProperty(StackDockStation.TAB_PLACEMENT,
                        TabPlacement.TOP_OF_DOCKABLE);

    CPerspective layout =
      control.getPerspectives().createEmptyPerspective();
    CGridPerspective center = layout.getContentArea().getCenter();
    Path path = new Path("custom", "flightlist");

    center.gridPlaceholder(0, 0, 1, 1, path);
    control.getPerspectives().setPerspective(layout, true);
    PlaceholderGrouping group = new PlaceholderGrouping(control, path);


    JPanel comboPanel1 = new JPanel();
//    comboPanel1.add(new JComboBox(new String[]
//      {
//        "OPTION1", "OPTION2"
//      }));

    DefaultSingleCDockable dockable =
      new DefaultSingleCDockable("1", null, "1", comboPanel1, null);

    CLocation location;
    if (control.getCDockableCount() > 0)
    {
      location =
        control.getCDockable(control.getCDockableCount() -
                             1).getBaseLocation();
    }
    else
    {
      location = control.getDefaultLocation();
    }

    dockable.setCloseable(false);
    dockable.setMaximizable(false);
    dockable.setMinimizable(false);
    dockable.setTitleShown(false);
    dockable.setGrouping(group);
    dockable.setSingleTabShown(true);
    dockable.setLocation(location);
    control.addDockable(dockable);


    dockable.setVisible(true);


    JPanel comboPanel2 = new JPanel();
    comboPanel2.add(new JComboBox(new String[]
      {
        "OPTION1", "OPTION2"
      }));


    DefaultSingleCDockable dockable2 =
      new DefaultSingleCDockable("2", null, "2", comboPanel2, null);

    CLocation location2;
    if (control.getCDockableCount() > 0)
    {
      location2 =
        control.getCDockable(control.getCDockableCount() -
                             1).getBaseLocation();
    }
    else
    {
      location2 = control.getDefaultLocation();
    }

    dockable2.setCloseable(false);
    dockable2.setMaximizable(false);
    dockable2.setMinimizable(false);
    dockable2.setTitleShown(false);
    dockable2.setGrouping(group);
    dockable2.setSingleTabShown(true);
    dockable2.setLocation(location2);
    control.addDockable(dockable2);


    dockable2.setVisible(true);
    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        pack();
        setVisible(true);
      }
    });
  }
  
  public static void main(String[] args)
  {
    new Test();
  }```

As a workaround, I've overridden the createPopup method of MetalComboBoxUI to invokeLater twice when .show() is called on it. Seems to handle this case, but is dirty and binds me to the Metal L&F.  Two bumps to the end of the event queue were necessary to resolve all other focus exchange events first.

Thanks for your help.

I currently do not fully understand why this is happening. I’ll do some debugging, but that has to wait until the weekend.

These focus issues are always tricky, and I will not give you any guarantee that I find a solution (actually, not finding a bugfix is far more likely than finding one)