Making Custom Stackstation or Grid Area Invisible

Hi Beni,

Is there a way to make a custom stack or gridarea invisible? I am using stacks/gridareas to create layouts for my dockables but need to make empty stacks/gridarea invisible so that the ones with dockables fill up the “empty spaces”. Also, I find that I can move empty stacks and dock them into other stacks. Is there a way to disable this? I don’t want my users to mess up the layout.

In general the “SingleParentRemover” is responsible for deleting any DockStation that is no longer required. Have you implemented a new one that does not remove your custom DockStations? The default implementation does not remove stations which were registered at a CControl (i.e. if “control.getStation( DockStation s ) != null”, “s” will not be deleted).

No, I have not down anything different from the example code found in basics/common/custom cstation. You can see the issue I get when you run the example and remove all the dockables from the stack. You are left with an empty stack that can still be moved around. Also it takes up space. I am wondering if theres a way to disable this as well as to reduce the size of the cstack or make it invisible so that the rest of the dockables can expand to fill up the space.

Ok. Then the question is: why do you use a custom stack/gridarea? What does it give you that you cannot accomplish without them?

To me it sounds a little bit as if there is a misunderstanding of how the framework works. Usually you don’t need custom CStations (maybe except a CWorkingArea), and most layouts can be created by using the frameworks ability to automatically create new StackDockStations where necessary.

Good question, but I think will need to elaborate a bit about my main problem to explain.

I have been facing a layout issue for the past month and have been trying all sorts of solutions. The main reason for this is that I am using another framework for gui generation and this framework requires is own set of functions to create panels. It requires a fixed grid layout of different sizes so that it can dynamically create panels. I am using docking frames to add docking to these panels.

The problem I am facing is that I am not sure how to specify a fixed layout for these panels that is flexible enough to expand and fill up spaces of missing panels that have not yet been created. I am now trying to create a grid of four customstations to represent a 4 by 4 grid. The problem I get is that the stations can be moved around. They also do not resize to take up the space of empty cstations.

I have tried placeholders before but they do not seem to work well if there were no dockables created beforehand.

I am sure there are other solutions that I am not aware of and would appreciate any suggestions you could give.

Sorry for the long post and thanks for your patience.

How is the name of that framework you use? Is it available for the public (without paying :stuck_out_tongue: )?

Placeholders add an object in the internal data structures that can later be replaced with a real Dockable. But these objects are not visible in any form on the UI, so I guess this is not the solution you are searching.
Also placeholders are deleted automatically unless either a Dockable with the correct name exists, a Single/MultipleCDockableFactory for the Dockable exists, or the MissingCDockableStrategy has been customized in order to validate the placeholder.

To use your initial proposal: you could replace the „SingleParentRemover“ by a class that removes your stations:

CControl control = ...

// Calling these methods will remove *any* DockStation that has 0 or 1 children. The removing will happen *right now*
control.getController().setSingleParentRemover( new SingleParentRemover() );
		
// This will reset the framework to its original behavior. Any DockStation that is registered at "control" cannot be removed automatically
control.getController().setSingleParentRemover( new CSingleParentRemover( control ) );```

It still is a strange solution. Would it be possible to:
- Use the other framework to build only the contents of the Dockables, but place the Dockable "manually" anyway?
- Use the other framework to build its panels on some dummy panel and then just copy the contents onto Dockables? Would also require to "manually" set the position of the Dockables.

Hi again Beni,

From your description of placeholders, it actually sounds like what I need. I am, however, struggling with the understanding of the implementation of it. I also think I did not really explain my problem properly.

Please allow me to try again but this time with pictures:

I need to create a grid layout with 4 segments (layout). Dockables are **labeled ** according to which segment they are to be added to. On startup, the other framework I am using will add dockables one by one (When all segments have dockables). Due to how that framework works however, some segments could be empty. The user can also close and open any dockable he likes.

I need the layout to be flexible enough to change accordingly. It should be able to do a few things:

  1. Vertically, if a segment does not have a dockable, the segment above or below it will expand and fill up the rest of the space. Like this.
  2. If a dockable is later added to the empty segment, it should conform to the layout resizing the neighbour segment back to the correct proportions.
  3. If there are 2 missing segments in a vertical, the segments beside it should expand horizontally and fill up their spaces. Like so.

Sorry that this is dragging out so long but I really need your help. Thanks in advance.

No need for apologizes. Do you know how many Dockables there are in advance? That would make things much easier. Anyway, I put up a test application, to show you how you could set placeholders and use them. Does that get close to your needs?


import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.MissingCDockableStrategy;
import bibliothek.gui.dock.common.MultipleCDockableFactory;
import bibliothek.gui.dock.common.MultipleCDockableLayout;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.event.CVetoClosingEvent;
import bibliothek.gui.dock.common.event.CVetoClosingListener;
import bibliothek.gui.dock.common.perspective.CControlPerspective;
import bibliothek.gui.dock.common.perspective.CGridPerspective;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;

public class SegmentTest {
	public static void main( String[] args ){
		JFrame frame = new JFrame();
		CControl control = new CControl( frame );
		
		// define a rule telling which placeholders should never be automatically removed
		control.setMissingStrategy( new MissingCDockableStrategy(){
			@Override
			public boolean shouldStoreSingle( String id ){
				// mark our placeholders, make sure they are not automatically deleted
				return "topleft".equals( id ) || "topright".equals( id ) || "bottomleft".equals( id ) || "bottomright".equals( id );
			}
			
			@Override
			public boolean shouldStoreMulti( String id ){
				// not called in this example
				return false;
			}
			
			@Override
			public <L extends MultipleCDockableLayout> boolean shouldCreate( String id, MultipleCDockableFactory<?, L> factory, String uniqueId, L data ){
				// not called in this example
				return false;
			}
			
			@Override
			public boolean shouldCreate( String id, MultipleCDockableFactory<?, ?> factory ){
				// not called in this example
				return false;
			}
		} );
		
		// build up a seemingly empty grid, first initialize the
		control.getContentArea();
		buildPerspective( control );
		
		frame.add( control.getContentArea() );
		frame.setBounds( 20, 20, 400, 400 );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		
		JFrame buttons = new JFrame( "Buttons" );
		buttons.setLayout( new GridLayout( 2, 2 ) );
		buttons.add( createButton( control, "topleft", "TL" ) );
		buttons.add( createButton( control, "topright", "TR" ) );
		buttons.add( createButton( control, "bottomleft", "BL" ) );
		buttons.add( createButton( control, "bottomright", "BR" ) );
		buttons.setBounds( 420, 20, 200, 200 );
		
		frame.setVisible( true );
		buttons.setVisible( true );
	}
	
	private static void buildPerspective( CControl control ){
		CControlPerspective perspectives = control.getPerspectives();
		CPerspective perspective = perspectives.createEmptyPerspective();
		CGridPerspective center = perspective.getContentArea().getCenter();
		
		// add dummy dockables to store the location
		center.gridAdd( 0, 0, 25, 25, new SingleCDockablePerspective( "topleft" ) );
		center.gridAdd( 0, 25, 25, 75, new SingleCDockablePerspective( "bottomleft" ) );
		center.gridAdd( 25, 0, 75, 60, new SingleCDockablePerspective( "topright" ) );
		center.gridAdd( 25, 60, 75, 40, new SingleCDockablePerspective( "bottomright" ) );
		
		// add the placeholders that need to remain
		center.gridPlaceholder( 0, 0, 25, 25, new SingleCDockablePerspective( "topleft" ) );
		center.gridPlaceholder( 0, 25, 25, 75, new SingleCDockablePerspective( "bottomleft" ) );
		center.gridPlaceholder( 25, 0, 75, 60, new SingleCDockablePerspective( "topright" ) );
		center.gridPlaceholder( 25, 60, 75, 40, new SingleCDockablePerspective( "bottomright" ) );
		
		// apply the new layout
		center.gridDeploy();
		
		perspective.storeLocations();
		
		perspectives.setPerspective( perspective, true );
	}
	
	private static JButton createButton( final CControl control, final String id, final String text ){
		JButton button = new JButton( text );
		button.addActionListener( new ActionListener(){
			@Override
			public void actionPerformed( ActionEvent e ){
				SingleCDockable initial = control.getSingleDockable( id );
				final DefaultSingleCDockable dockable;
				
				// If there is not yet a dockable for "id" we can now just create it and add it ...
				if( initial == null ){
					dockable = new DefaultSingleCDockable( id, text );
					dockable.setCloseable( true );
					control.addDockable( dockable );
					dockable.setVisible( true );
				}
				else{
					// .. but if there is one we search a new unique id and add a new dockable close to 
					// the existing one.
					int index = 0;
					while( control.getSingleDockable( id + index ) != null ){
						index++;
					}
					dockable = new DefaultSingleCDockable( id + index, text + " " + index );
					dockable.setCloseable( true );
					control.addDockable( dockable );
					dockable.setLocation( initial.getBaseLocation().aside() );
					dockable.setVisible( true );
				}
				
				// And we should clean up any dockable we no longer need
				dockable.addVetoClosingListener( new CVetoClosingListener(){
					@Override
					public void closing( CVetoClosingEvent event ){
						// ignore	
					}
					
					@Override
					public void closed( CVetoClosingEvent event ){
						control.removeDockable( dockable );
					}
				} );
			}
		} );
		return button;
	}
}