CPerspective usage

Hi,

Could you please advice when to use CPerspective, CControlPerspective, CStackPerspective,… instead of CContentArea, CControl, CWorkingArea?

Regards,

You will always need to use CControl/CContentArea/CWorkingArea. The CPerspective only allows you to set up one or many layouts (position, size, relationship of Dockables and Stations) without pushing around the CDockables directly. Perspectives are an optional feature, you will never be forced to use them.

It may be interesting for a bigger application where you don’t want to create any CDockables yourself, and rather use some factories which do that automatically. Then you use the perspectives to place identifiers at locations where you want the Dockables to appear, and the framework will create them once they are really needed.

They can also be helpful if you want to present several layouts to the user, from which he can choose one. With perspectives you could set up the layouts without the overhead of all the Swing-Components that are created when working with the real Dockables and Stations.

Check out the tutorial project (directory “demo” in the SVN). In the package tutorial.common.guide there are some examples, like the one below:


import java.awt.BorderLayout;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;

import tutorial.support.ColorSingleCDockable;
import tutorial.support.JTutorialFrame;
import tutorial.support.Tutorial;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.SingleCDockableFactory;
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.CStackPerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.util.Filter;

@Tutorial(title="Perspectives (Introduction)", id="PerspectivesIntroduction")
public class PerspectivesIntroduction {
	public static void main( String[] args ){
		/* Perspectives allow clients to modify and create layouts, the location, size
		 * and relation of Dockables.
		 * In this example we set up the layout of an application using only perspectives.
		 * 
		 * Perspectives act as wrapper around a CControl, so in order to access them
		 * we first need to set up a CControl. */
		
		JTutorialFrame frame = new JTutorialFrame( PerspectivesIntroduction.class );
		CControl control = new CControl( frame );
		frame.destroyOnClose( control );
		
		/* Because perspectives copy some settings from the CControl, it is a good idea 
		 * to first register any factories and register any root-stations before accessing
		 * the perspectives.  */
		ColorFactory colorFactory = new ColorFactory();
		control.addSingleDockableFactory( colorFactory, colorFactory );
		frame.add( control.getContentArea(), BorderLayout.CENTER );
		
		/* A CControlPerspective is a view of a CControl through the eyes of the 
		 * perspective-API. It can only be accessed by calling "getPerspectives". */
		CControlPerspective perspectives = control.getPerspectives();
		
		/* Since we want to create a new layout, we need to create a new perspective. */
		CPerspective perspective = perspectives.createEmptyPerspective();
		
		/* The perspective-API offers different views for different elements of the Common-API.
		 * The CGridPerspective represents a CGridArea, in this case the one area that is in
		 * the middle of our JFrame. */
	    CGridPerspective center = perspective.getContentArea().getCenter();
	    /* The CGridPerspective works just like a CGrid. We add views of SingleCDockables.
	     * The only information that is stored of a SingleCDockable is its unique identifier,
	     * so there is no need to create any subclasses or set any properties other than 
	     * the id. */
	    center.gridAdd( 0, 0, 50, 50, new SingleCDockablePerspective( "Red" ) );
	    center.gridAdd( 50, 0, 50, 50, new SingleCDockablePerspective( "Green" ) );
	    center.gridAdd( 0, 50, 50, 50, new SingleCDockablePerspective( "Blue" ) );
	    
	    /* If we want to combine several dockable we can create a CStackPerspective. */
	    CStackPerspective stack = new CStackPerspective();
	    stack.add( new SingleCDockablePerspective( "White" ) );
	    stack.add( new SingleCDockablePerspective( "Black" ) );
	    stack.setSelection( stack.getDockable( 0 ) );
	    /* Like many classes in Common, CGridPerspective is a wrapper around a class of Core.
	     * In this case the underlying class can be accessed with "grid()" and can be
	     * modified directly. */
	    center.grid().addDockable( 50, 50, 50, 50, stack );
	    
	    /* And we can access other areas than the center area as well, for example the
	     * minimize-area at the west side. */
	    perspective.getContentArea().getWest().add( new SingleCDockablePerspective( "Yellow" ) );
	    
	    /* By storing the perspective with an identifier we can later access it again. */
	    perspectives.setPerspective( "example", perspective );
	    
	    /* After storing the perspective we can access load it from the CControl */
		control.load( "example" );
	    
	    frame.setVisible( true );
	}
	
	/* This factory and filter creates new SingleCDockables with some panel that has some
	 * special color set as background. */
	private static class ColorFactory implements SingleCDockableFactory, Filter<String>{
		private Map<String, Color> colors = new HashMap<String, Color>();
		
		public ColorFactory(){
			colors.put( "Red", Color.RED );
			colors.put( "Green", Color.GREEN );
			colors.put( "Blue", Color.BLUE );
			colors.put( "Yellow", Color.YELLOW );
			colors.put( "White", Color.WHITE );
			colors.put( "Black", Color.BLACK );
		}
		
		public boolean includes( String item ){
			return colors.containsKey( item );
		}
		
		public SingleCDockable createBackup( String id ){
			return new ColorSingleCDockable( id, colors.get( id ) );
		}
	}
}

Thank you

Hi Benji,

I am trying to use the perspectives and thus followed the tutorial, but it seems I missed something :slight_smile:

I have a java.awt.Component containg the view I want to add to a perspective. How do I do that. Prior to using perspectives, I just created a CDockable from the component:

final DefaultSingleCDockable dockable = new DefaultSingleCDockable(String.valueOf(this.counter
                .incrementAndGet()), componentTitle, componentToAdd);

But with perspectives, I can only add SingleCDockablePerspective and I don’t know how to add a Component in this object. I tried using the factory, but it performs a switch on a String to create a SingleCDockable and I want to pass any kind of Component. How can I do this?

Thank you
Ben

The perspective does neither know nor care about the actual view, any actual Dockable or Component is ignored. It may help to know, that editing the xml file that stores the layout in some text editor, is very close to working with perspectives. Or in other words: perspectives do not offer anything, that you could not accomplish by directly editing the xml file. And in the xml file you would not be able to insert some Components, simply because it is a text file and not an application. The same goes for perspectives.

With perspectives you can only add a SingleCDockablePerspective (it’s like a special xml tag), and this SingleCDockablePerspective tells the framework that sometime in the future, there might exist a SingleCDockable that has the same unique identifier as the SingleCDockablePerspective.

So in the end you’ll need to perform these steps.

  • Know that you will have some Component, and that this component will have some unique identifier “x”. You need to know the identifier in advance.
  • a) Either register a factory (like in the code above), that knows how to convert the identifier “x” into a Dockable …
  • b) … or just create the Dockable and register it directly. That would work like in your old code, the only difference is, that your unique identifier is not “randomly” generated. It’s always going to be “x”. Case a) usually is the more efficient approach.
  • And then create a perspective, inserting a SingleCDockablePerspective with identifier “x” at the location where the Dockable should appear.

It all boils down to: “you need to know which unique identifiers are used by which Dockable, and these identifiers must always remain the same”. If you can’t do that, then you can’t use perspectives.

Ok, thanks for your answer, I will try that

I tryed it and it worked, so thanks again Beni.

However, I have the following NPE, where does it come from?
I use Docking Frames 1.1.1 preview 6e


Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
	at bibliothek.extension.gui.dock.theme.eclipse.stack.EclipseTabPaneContent.paintBackground(EclipseTabPaneContent.java:58)
	at bibliothek.gui.dock.util.BackgroundPanel.paintComponent(BackgroundPanel.java:118)
	at javax.swing.JComponent.paint(JComponent.java:1029)
	at bibliothek.gui.dock.util.BackgroundPanel.paint(BackgroundPanel.java:109)
	at javax.swing.JComponent.paintChildren(JComponent.java:862)
	at bibliothek.gui.dock.util.BackgroundPanel.paintChildren(BackgroundPanel.java:167)
	at javax.swing.JComponent.paint(JComponent.java:1038)
	at bibliothek.gui.dock.util.BackgroundPanel.paint(BackgroundPanel.java:109)
	at javax.swing.JComponent.paintChildren(JComponent.java:862)
	at javax.swing.JComponent.paint(JComponent.java:1038)
	at javax.swing.JComponent.paintChildren(JComponent.java:862)
	at javax.swing.JComponent.paint(JComponent.java:1038)
	at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
	at javax.swing.JComponent.paintChildren(JComponent.java:862)
	at bibliothek.gui.dock.util.BackgroundPanel.paintChildren(BackgroundPanel.java:167)
	at javax.swing.JComponent.paint(JComponent.java:1038)
	at bibliothek.gui.dock.util.BackgroundPanel.paint(BackgroundPanel.java:109)
	at bibliothek.extension.gui.dock.theme.eclipse.displayer.NoTitleDisplayer.paint(NoTitleDisplayer.java:240)
	at javax.swing.JComponent.paintChildren(JComponent.java:862)
	at bibliothek.gui.dock.util.BackgroundPanel.paintChildren(BackgroundPanel.java:167)
	at javax.swing.JComponent.paint(JComponent.java:1038)
	at bibliothek.gui.dock.util.BackgroundPanel.paint(BackgroundPanel.java:109)
	at javax.swing.JComponent.paintChildren(JComponent.java:862)
	at javax.swing.JComponent.paint(JComponent.java:1038)
	at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
	at javax.swing.JComponent.paintToOffscreen(JComponent.java:5124)
	at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
	at javax.swing.RepaintManager.paint(RepaintManager.java:1225)
	at javax.swing.JComponent._paintImmediately(JComponent.java:5072)
	at javax.swing.JComponent.paintImmediately(JComponent.java:4882)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:786)
	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:714)
	at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:694)
	at javax.swing.RepaintManager.access$700(RepaintManager.java:41)
	at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1636)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:646)
	at java.awt.EventQueue.access$000(EventQueue.java:84)
	at java.awt.EventQueue$1.run(EventQueue.java:607)
	at java.awt.EventQueue$1.run(EventQueue.java:605)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:616)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

You always have this exception? Or just sometimes? In the later case it could be a race condition. I’ll have a look at the code.

I always have this exception in a given configuration where I place two components at the same place on the grid.

I could reproduce the bug, but only bu using multiple threads. So it seems to be a race condition (the framework does not support multi threading). In my case calling the “dangerous” code in the EDT solved the issue:


import java.awt.EventQueue;

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

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.gui.dock.common.theme.ThemeMap;

public class Dock18 {
	public static void main( String[] args ){
		JFrame frame = new JFrame();
		final CControl control = new CControl( frame );
		frame.add( control.getContentArea() );
		control.setTheme( ThemeMap.KEY_ECLIPSE_THEME );
		CGrid grid = new CGrid( control );
		grid.add( 0, 0, 10, 10, new DefaultSingleCDockable( "a", "AAA" ));
		grid.add( 0, 10, 10, 10, new DefaultSingleCDockable( "b", "BBB" ));
		control.getContentArea().deploy( grid );
		frame.setBounds( 20, 20, 400, 400 );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setVisible( true );
		
		// calling the code in the EDT is ok
		EventQueue.invokeLater( new Runnable(){
			@Override
			public void run(){
				CPerspective p = control.getPerspectives().createEmptyPerspective();
				p.getContentArea().getCenter().gridAdd( 0, 0, 1, 1, new SingleCDockablePerspective( "a" ) );
				p.getContentArea().getCenter().gridAdd( 0, 0, 1, 1, new SingleCDockablePerspective( "b" ) );
				control.getPerspectives().setPerspective( p, true );
			}
		} );
		
		// if called like this, it's a race condition
//		CPerspective p = control.getPerspectives().createEmptyPerspective();
//		p.getContentArea().getCenter().gridAdd( 0, 0, 1, 1, new SingleCDockablePerspective( "a" ) );
//		p.getContentArea().getCenter().gridAdd( 0, 0, 1, 1, new SingleCDockablePerspective( "b" ) );
//		control.getPerspectives().setPerspective( p, true );
	}
}