Focus changes on maximized stack

Hi Beni,

I found a bug that changes the focus of dockables on a maximized StackDockStation when an external dockable is closed.
The reproduction is as follows:

  1. run the below code
  2. a frame with some dockables is open, the dockables are maximized (by maximizing the red dockable), the last one is focused (Red4), there is one additional external dockable (green)
  3. close the external green dockable -> the focus on the stack switches from Red4 to Blue

It only happens if the stack is maximized.

kind regards,
Maciej Modelski

package test;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import bibliothek.extension.gui.dock.theme.flat.FlatStationPaint;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.theme.CEclipseTheme;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.gui.dock.dockable.ScreencaptureMovingImageFactory;
import bibliothek.gui.dock.station.screen.ScreenDockProperty;
import bibliothek.gui.dock.station.split.SplitDockPathProperty;

public class Dock112
{
  public static void main(String[] args)
  {
    // setting up frame and controller
    JFrame frame = new JFrame();
    final CControl control = new CControl(frame);
    control.setTheme(ThemeMap.KEY_ECLIPSE_THEME);

    ((CEclipseTheme)control.getController().getTheme()).intern().setPaint(new FlatStationPaint());
    ((CEclipseTheme)control.getController().getTheme()).intern().setMovingImageFactory(new ScreencaptureMovingImageFactory(new Dimension(300, 200)));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(300, 300, 700, 400);

    CGrid grid = new CGrid(control);

    control.getContentArea().deploy(grid);
    
    final SplitDockStation split = control.getContentArea().getCenter();
    

    DefaultSingleCDockable red = create(control, "Red", Color.RED);
    control.addDockable(red);
    split.drop(red.intern(), new SplitDockPathProperty());

    DefaultSingleCDockable blue = create(control, "Blue", Color.BLUE);
    control.addDockable(blue);
    split.drop(blue.intern(), new SplitDockPathProperty());

    
    for (int i=0; i<5; i++)
    {
        DefaultSingleCDockable dock = create(control, "Red" + i, Color.RED);
        control.addDockable(dock);
        split.drop(dock.intern(), new SplitDockPathProperty());    	       
    }
    
    ScreenDockStation screen  = (ScreenDockStation) control.getStation(CControl.EXTERNALIZED_STATION_ID).getStation();
    
    DefaultSingleCDockable green = create(control, "Green", Color.GREEN);
    control.addDockable(green);
    screen.drop(green.intern(), new ScreenDockProperty(100, 100, 200, 200));

    red.setExtendedMode(ExtendedMode.MAXIMIZED);
    
    frame.getContentPane().add(control.getContentArea(), BorderLayout.CENTER);
    frame.setVisible(true);
    
    
  }

  public static DefaultSingleCDockable create(CControl control, String title, Color color)
  {
    final JPanel panel = new JPanel();
    panel.setOpaque(true);
    panel.setBackground(color);

    final DefaultSingleCDockable singleDockable = new DefaultSingleCDockable(title, title, panel);
    singleDockable.setCloseable(true);

    return singleDockable;
  }

}

Sorry, I did not see your message. I’ll try your example ASAP (may need a few days).

Just switching the order in which the code is executed, helped me. I’ve moved “frame.getContentPane().add(…)” a bit up, and the issue was gone.

[Edit: no it does not help, the focus is just randomly given to another dockable :-/ ]

[Edit2: but it is an issue concerning the focus history, because calling “red.toFront” makes the bug disappear.]


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

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

import bibliothek.extension.gui.dock.theme.flat.FlatStationPaint;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.theme.CEclipseTheme;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.gui.dock.dockable.ScreencaptureMovingImageFactory;

public class Dock112 {
	public static void main( String[] args ) throws Exception {
		// setting up frame and controller
		final JFrame frame = new JFrame();
		final CControl control = new CControl( frame );
		control.setTheme( ThemeMap.KEY_ECLIPSE_THEME );

		((CEclipseTheme) control.getController().getTheme()).intern().setPaint( new FlatStationPaint() );
		((CEclipseTheme) control.getController().getTheme()).intern().setMovingImageFactory( new ScreencaptureMovingImageFactory( new Dimension( 300, 200 ) ) );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setBounds( 300, 300, 700, 400 );

		frame.getContentPane().add( control.getContentArea(), BorderLayout.CENTER ); // <-- do this first

		DefaultSingleCDockable red = create( control, "Red", Color.RED );
		control.addDockable( red );
		red.setLocation( CLocation.base().normal() );
		red.setVisible( true );

		DefaultSingleCDockable blue = create( control, "Blue", Color.BLUE );
		control.addDockable( blue );
		blue.setLocation( CLocation.base().normal().stack() );
		blue.setVisible( true );

		for( int i = 0; i < 5; i++ ) {
			DefaultSingleCDockable dock = create( control, "Red" + i, Color.RED );
			control.addDockable( dock );
			dock.setLocation( CLocation.base().normal().stack() );
			dock.setVisible( true );
		}

		DefaultSingleCDockable green = create( control, "Green", Color.GREEN );
		control.addDockable( green );
		green.setLocation( CLocation.external( 100, 100, 200, 200 ) );
		green.setVisible( true );

		frame.setVisible( true );

	}

	public static DefaultSingleCDockable create( CControl control, String title, Color color ) {
		final JPanel panel = new JPanel();
		panel.setOpaque( true );
		panel.setBackground( color );

		final DefaultSingleCDockable singleDockable = new DefaultSingleCDockable( title, title, panel );
		singleDockable.setCloseable( true );

		return singleDockable;
	}

}```

*** Edit ***

I think I found a solution: the history of the focused dockables now also contains those Dockables that did not yet have the focus. I've checked in an uploaded a new version of the framework, containing this bugfix. [1.1.2p14c](http://dock.javaforge.com/dockingFrames_v1.1.2/df_1.1.2p14c.zip)

Hi Beni,

I tried the 112p14c and it still reproduces.
In your example you are missing the crucial line from my code: red.setExtendedMode(ExtendedMode.MAXIMIZED);
If you put that line in line 51 of your code it should reproduce.
The bug appears when the stack is maximized…

kind regards,
Maciej Modelski

Just confirming that I still remember the bug - but I did not have time to work on it yet.

Hi Beni,

Do you know approximately when you will have time to look at the bug again? :slight_smile:

kind regards,
Maciej Modelski

Sorry for the long wait, I admit, I forgot.

After revisiting the example I found another bug in the framework, but it was not related to this issue.

There are some differences between our examples. You make heavy use of methods like “SplitDockStation.drop”, while my example uses code like “red.setLocation( CLocation.base().normal() ); red.setVisible( true );”. This actually matters, because “setVisible” does a bit more than just “drop” would do: it also sets up the history of locations of the Dockable. I would recommend you use the methods provided by the classes/interfaces of “common” as often as possible, because otherwise the features of “common” might not work properly.

Also: your code is not executed in the EDT. And this causes some mischief, because when you added your first few Dockables, the EDT will already update the focus history - and then completely miss the fact that what it considers to be the focused Dockable (e.g. “blue”) will be covered by another Dockable later on. If you put your code into a Runnable and call “EventQueue.invokeLater”, then the focus will work as expected.

In short: make use of the methods of “common” to ensure that all features work properly, and only access the framework from within the EDT. Actually, my example making full use of the common-framework accidentally also makes use of some safeguards against accesses from outside of the EDT, and that helps my example working properly too.

	public static void main( String[] args ) {
		// setting up frame and controller
		
		EventQueue.invokeLater( new Runnable() {
			@Override
			public void run() {
				
				JFrame frame = new JFrame();
				final CControl control = new CControl( frame );
				control.setTheme( ThemeMap.KEY_ECLIPSE_THEME );
				
				((CEclipseTheme) control.getController().getTheme()).intern().setPaint( new FlatStationPaint() );
				((CEclipseTheme) control.getController().getTheme()).intern().setMovingImageFactory( new ScreencaptureMovingImageFactory( new Dimension( 300, 200 ) ) );
				frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
				frame.setBounds( 300, 300, 700, 400 );
				
				CGrid grid = new CGrid( control );
				
				control.getContentArea().deploy( grid );
				
				final SplitDockStation split = control.getContentArea().getCenter();
				
				DefaultSingleCDockable red = create( control, "Red", Color.RED );
				control.addDockable( red );
				split.drop( red.intern(), new SplitDockPathProperty() );
				
				DefaultSingleCDockable blue = create( control, "Blue", Color.BLUE );
				control.addDockable( blue );
				split.drop( blue.intern(), new SplitDockPathProperty() );
				
				for( int i = 0; i < 5; i++ ) {
					DefaultSingleCDockable dock = create( control, "Red" + i, Color.RED );
					control.addDockable( dock );
					split.drop( dock.intern(), new SplitDockPathProperty() );
				}
				
				ScreenDockStation screen = (ScreenDockStation) control.getStation( CControl.EXTERNALIZED_STATION_ID ).getStation();
				
				DefaultSingleCDockable green = create( control, "Green", Color.GREEN );
				control.addDockable( green );
				screen.drop( green.intern(), new ScreenDockProperty( 100, 100, 200, 200 ) );
				
				red.setExtendedMode( ExtendedMode.MAXIMIZED );
				
				frame.getContentPane().add( control.getContentArea(), BorderLayout.CENTER );
				frame.setVisible( true );
			}
		} );
		

	}

	public static DefaultSingleCDockable create( CControl control, String title, Color color ) {
		final JPanel panel = new JPanel();
		panel.setOpaque( true );
		panel.setBackground( color );

		final DefaultSingleCDockable singleDockable = new DefaultSingleCDockable( title, title, panel );
		singleDockable.setCloseable( true );

		return singleDockable;
	}

}```