Hi,
About 9 months ago, I tried to integrate DockingFrames into my favorite text editor, jEdit. At the time, I had to struggle a lot (and use your support a lot…) with the “core” library because the docking framework in jEdit handles only part of the main window (JFrame), not the entire window, and CControl did not support such a configuration.
Has anything changed, or do you plan to provide support for this in the “common” library soon? Using the core library and a lot of complex code, I got it working to some extent, but there were some problems and I lost motivation to continue this effort due to the amount of time and complexity required.
If I can use the “common” library, I will give it another try, as I really like this docking framework and its support for themes.
Shlomy
In the current version (1.0.7) CControl no longer requires a JFrame but a WindowProvider. A WindowProvider can - but does not have to - represent a JFrame. There are several WindowProviders implemented, one for Applets, one for always returning the ancestor of some Component, … Also the WindowProvider allows to exchange the frame any time. CControl supports not knowing any Window, but that will result in some bad effects: e.g. dialogs created by the framework „hiding“ behind the applications frame.
So yes, something did change
Hi,
Thanks for the quick reply. While this answers my question, I just tried to use the Common interface and found it to be too complex without a working example. Is there some example / demo for the Common interface that I can use as a reference?
Thanks,
Shlomy
There is kind of an introduction here.
Also the applications „commonLayout“, „paint“ and „sizeAndColor“ are built with Common. These applications are in the big zip file on the page (this one). Personally I think for the beginning „paint“ is the best example.
More specific questions would bring you better answers
Thanks. I will check these applications.
The introduction you specified is very partial - many important parts of the code appear as “…”. For example, I did not understand how the factory for MultipleCDockable can create the dockables - since the create() method does not specify which dockable to create (e.g. by name / id).
- Factory: creates empty layout -> 2. Layout: reads data -> 3. Factory: converts layout into Dockable
First the factory "create"s a “MultipleCDockableLayout”. The layouts “read”-methods is called to read earlier stored data (assuming there is such a thing), storing the data in the layout-object itself. Then the factories “read” method is called, this method can then create the Dockable using the information provided by the layout-object.
Which Dockable to create depends on the layout (of type MultipleCDockableLayout). The layout itself was created in an earlier run of the application by the same factory (or to be more precise: a factory with the same id). All information required to create a Dockable must be stored in the layout.
I tried to use “paint” as an example, but this one is too large to be used as an example. Could you provide a very simple example for the “common” API, like the “Simple example” in the help page, that shows how to use the “core” API? Basically what I want is something like BorderLayout - a center area, which is the edit window, surrounded by docking windows, letting the user drag the docking windows anywhere around the center area, with the option to save & load the layout.
The DockingFrames manages only part of the frame in my application, and none of the docking windows is “fixed” - i.e., at any time any of the docking windows may be created or removed.
Thanks!
Well… scroll down on that very same page, there is a second example It’s called „How to write a small application with common“.
As for saving the layout: the methods „write“ and „read“ of CControl do this. „Read“ requires that you first register your CDockables or that you provide factories.
You say at any time Dockables can join or leave the pool? Assuming they are all of the same type, then they would be perfectly fit to be MultipleCDockables.
Right, sorry I skipped that simple example.
What seems to be missing in all these examples is the relation between dockables and stations. The only example I saw was how to add dockables to a CGrid and then “deploy” the grid in the content area.
I’d like to have a main component at the center, surrounded by docking windows, which should be able to move anywhere around the main component. Which layout is appropriate for this? A CContextArea surrounded by 4 CGridArea objects? Most of the time, the dockables in each side of the main component will appear in a tabbed pane, but I’d like to allow the user to split the area between two different docking windows - and not necessarily split in the middle, e.g. 30% for one, 70% for the other.
Can you provide the “simple examine using common” with small modifications such that:
- The docking windows are multi-, not single-
- There is a main component in the middle
if you can provide me with such an example, this would be great.
Dockables are children, stations are parent. Each station has a specific layout and some mechanism to interact with the user. There is not much communication between Dockable and station.
In general you need few stations, you can create most of the desired layouts with a single CGridArea.
There are different solutions for a “main component”.
One of them is to use a CWorkingArea with only one child (in the example below I put 2 children in it). The CWorkingArea will ensure that no Dockable is put over that child. Disabling things like the title (AbstractCDockable#setTitleShown) or minimizing/externalizing (DefeaultCDockable#setMinimizable etc.) will “hide” the fact, that the main-component is a Dockable.
Another solution could be to make a CDockable non-stackable (DefaultCDockable#setStackable) in order to prevent other Dockables from beeing dropped over it.
I’ve put up an example the way you asked for. I used two Dockables as main-components. You could also replace the “mainArea” by a single Dockable for which you call “setStackable( false )”.
import java.awt.GridLayout;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.CWorkingArea;
import bibliothek.gui.dock.common.DefaultMultipleCDockable;
import bibliothek.gui.dock.common.MultipleCDockableFactory;
import bibliothek.gui.dock.common.MultipleCDockableLayout;
import bibliothek.gui.dock.common.menu.CLayoutChoiceMenuPiece;
import bibliothek.gui.dock.facile.menu.RootMenuPiece;
import bibliothek.util.xml.XElement;
public class Dock {
public static void main( String[] args ){
JFrame frame = new JFrame( "Demo" );
CControl control = new CControl( frame );
// Initialize factory
ColorFactory factory = new ColorFactory();
control.addMultipleDockableFactory( "color", factory );
// setup basic frame
frame.add( control.getContentArea() );
// the main area, no Dockable can be dragged into this area unless
// explicitly allowed
CWorkingArea mainArea = control.createWorkingArea( "main-area" );
// setup basic layout with help of a CGrid
CGrid grid = new CGrid( control );
grid.add( 0, 0, 1, 2, new ColorDockable( factory, "Red 1", Color.RED ) );
grid.add( 0, 0, 1, 2, new ColorDockable( factory, "Red 2", Color.RED ) );
grid.add( 1, 0, 2, 2, mainArea );
grid.add( 2, 0, 1, 3, new ColorDockable( factory, "Green 1", Color.GREEN ) );
grid.add( 2, 0, 1, 3, new ColorDockable( factory, "Green 2", Color.GREEN ) );
grid.add( 2, 0, 1, 3, new ColorDockable( factory, "Green 3", Color.GREEN ) );
grid.add( 0, 2, 2, 1, new ColorDockable( factory, "Blue 1", Color.BLUE ) );
grid.add( 0, 2, 2, 1, new ColorDockable( factory, "Blue 2", Color.BLUE ) );
grid.add( 0, 2, 2, 1, new ColorDockable( factory, "Blue 3", Color.BLUE ) );
control.getContentArea().deploy( grid );
// Add additional dockables directly
ColorDockable yellow1 = new ColorDockable( factory, "Yellow 1", Color.YELLOW );
yellow1.setLocation( CLocation.base().normal().rectangle( 0, 0, 0.25, 0.25 ) );
control.add( yellow1 );
yellow1.setVisible( true );
// Add elements to the main area
ColorDockable main1 = new ColorDockable( factory, "Main 1", Color.BLACK );
ColorDockable main2 = new ColorDockable( factory, "Main 2", Color.DARK_GRAY );
control.add( main1 );
control.add( main2 );
main1.setLocation( mainArea.getStationLocation() );
main2.setLocation( mainArea.getStationLocation() );
main1.setVisible( true );
main2.setVisible( true );
// a menu to play around with the layout (no persistant storage)
JMenuBar menuBar = new JMenuBar();
RootMenuPiece menu = new RootMenuPiece( "Layout", false, new CLayoutChoiceMenuPiece( control, true ) );
menuBar.add( menu.getMenu() );
frame.setJMenuBar( menuBar );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setBounds( 20, 20, 600, 600 );
frame.setVisible( true );
}
}
// our Dockable
class ColorDockable extends DefaultMultipleCDockable{
private Color color;
public ColorDockable( ColorFactory factory, String title, Color color ){
super( factory );
setTitleText( title );
this.color = color;
JPanel panel = new JPanel();
panel.setOpaque( true );
panel.setBackground( color );
setLayout( new GridLayout( 1, 1 ) );
add( panel );
}
public Color getColor(){
return color;
}
}
// Factory to create ColorDockables
class ColorFactory implements MultipleCDockableFactory<ColorDockable, ColorLayout>{
public ColorLayout create(){
return new ColorLayout( null, null );
}
public ColorDockable read( ColorLayout layout ){
return new ColorDockable( this, layout.getTitle(), layout.getColor() );
}
public ColorLayout write( ColorDockable dockable ){
return new ColorLayout( dockable.getTitleText(), dockable.getColor() );
}
}
//Layout information about a ColorDockable
class ColorLayout implements MultipleCDockableLayout{
private Color color;
private String title;
public ColorLayout( String title, Color color ){
this.title = title;
this.color = color;
}
public String getTitle(){
return title;
}
public Color getColor(){
return color;
}
public void readStream( DataInputStream in ) throws IOException{
title = in.readUTF();
color = new Color( in.readInt() );
}
public void readXML( XElement element ){
title = element.getElement( "title" ).getString();
color = new Color( element.getElement( "rgb" ).getInt() );
}
public void writeStream( DataOutputStream out ) throws IOException{
out.writeUTF( title );
out.writeInt( color.getRGB() );
}
public void writeXML( XElement element ){
element.addElement( "title" ).setString( title );
element.addElement( "rgb" ).setInt( color.getRGB() );
}
}```
Thanks a lot, just what I needed!
Shlomy
One small issue - setTitleShown(false) on the main window (the editor window) works fine in all themes except eclipse - which happens to be my preferrable theme.
Yes, I forgot. But since I’m currently rewriting the whole tab-mechanism anyway, I can upgrade the eclipse theme as well. So it will work in the next release (still aiming to the end of this month).
Thanks.
I can’t figure out how to move a dockable programmatically. My editor defines some API for setting the position of a dockable window at either the top, bottom, left or right of the editor window, and I would like to support this API when working with DockingFrames.
I currently use what you suggested - grid.add(…) to add the dockables the first time, and this works fine. However, when asked to move a dockable to another position, I would like to use a similar thing, but it doesn’t work. More precisely:
- Just calling grid.add(…) again on the same dockable with a different grid position has no visible effects.
- Calling grid.add(…) again and then using “deploy” on it generates an exception because the dockables are already registered.
Any advice?
Thanks,
Shlomy
Also, I get an NPE when calling CControl.write:
AWT-EventQueue-0: Exception in thread “AWT-EventQueue-0”
AWT-EventQueue-0: java.lang.NullPointerException
AWT-EventQueue-0: at java.io.DataOutputStream.writeUTF(Unknown Source)
AWT-EventQueue-0: at java.io.DataOutputStream.writeUTF(Unknown Source)
AWT-EventQueue-0: at dockingFrames.DfWindowManager$JEditDockableLayout.writeStream(DfWindowManager.java:291)
AWT-EventQueue-0: at bibliothek.gui.dock.common.intern.CommonMultipleDockableFactory.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.common.intern.CommonMultipleDockableFactory.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.layout.DockSituation.writeCompositionStream(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.layout.DockSituation.writeCompositionStream(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.layout.DockSituation.writeCompositionStream(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.layout.DockSituation.writeComposition(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.frontend.Setting.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.common.intern.CSetting.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.DockFrontend.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.DockFrontend.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.common.CControl$4.write(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.support.util.ApplicationResourceManager.writeStream(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.support.util.ApplicationResourceManager.writeFile(Unknown Source)
AWT-EventQueue-0: at bibliothek.gui.dock.common.CControl.write(Unknown Source)
AWT-EventQueue-0: at dockingFrames.DfDockingLayout.saveLayout(DfDockingLayout.java:42)
AWT-EventQueue-0: at dockingFrames.DfDockingLayout.(DfDockingLayout.java:22)
AWT-EventQueue-0: at dockingFrames.DfWindowManager.getDockingLayout(DfWindowManager.java:198)
AWT-EventQueue-0: at org.gjt.sp.jedit.View.getViewConfig(View.java:1125)
AWT-EventQueue-0: at org.gjt.sp.jedit.PerspectiveManager.savePerspective(PerspectiveManager.java:181)
Is it something I am doing wrong?
CControl.writeXML also generates an exception.
Shlomy
[QUOTE=shlomy]Thanks.
I can’t figure out how to move a dockable programmatically. My editor defines some API for setting the position of a dockable window at either the top, bottom, left or right of the editor window, and I would like to support this API when working with DockingFrames.
I currently use what you suggested - grid.add(…) to add the dockables the first time, and this works fine. However, when asked to move a dockable to another position, I would like to use a similar thing, but it doesn’t work. More precisely:
- Just calling grid.add(…) again on the same dockable with a different grid position has no visible effects.
- Calling grid.add(…) again and then using “deploy” on it generates an exception because the dockables are already registered.
Any advice?
Thanks,
Shlomy[/QUOTE]
You could use the method “setLocation” of CDockable. This method requires a CLocation object. In CLocation there are several factory methods to create new CLocations.
Example:
CLocaion north = CLocation.base( content ).normal().north( 0.25 );
dockable.setLocation( north );```
There is also some explanation in the guid for common, page 15.
To the NPE: is your “JEditDockableLayout” perhaps writing a null-String into the DataOutputStream?
I read the part about setLocation, but I don’t understand how to map CGrid cells to locations. For example, I initially used something like:
grid.add(1, 3, 3, 1, dockable);
to add a dockable to the “south” of the main window.
Now, I want to move another dockable, which was located in another place on the grid (e.g. to the “north” of the main window) to the same area. How do I create a location for these grid cells?
I think you missunderstood what a CGrid does:
- A CGrid collects Dockables and their position/sizes.
- Once the deploy-method is called, the grid creates a tree of dockables
- The tree is transfered to a CGridArea or CContentArea, where it is further processed
- Tree and CGrid become garbage and are deleted
- The CGridArea or CContentArea continues to exist with an initial layout fitting to the CGrid you created earlier
All further operations must be directed to the CGridArea or CContentArea, CGrids no longer are of any use.
The CGridArea itself is not a grid of cells (altough for the user it looks like it were), but a tree where Dockables are leafs, and the dividers between Dockables (or groups of Dockables) are nodes.
Now what is “the north” of the main-component? It could just be anything above the main-component, then a location like in my previous post could do the trick. Notice that this area collides with similar definitions of east and west…
Or it is the area directly above the main-component. Then asking the main component for its location and transform this location would help, but actually I wouldn’t know how to do this and the framework does not support it.
There are also layouts where “move to the north” does not make much sense. Imagine a layout like this one, where there are Dockables A, B and C north of main:
A | B | C
-------------
main
Now where to put a Dockable D?
Personally I would not use this API, it does not sufficiently describe the possibilities of the DF. It would be better if users learn to drag and drop Dockables.
Thanks for the great support. Now:
- Regarding the persistence: You were right, my layout object wrote null to the output stream. It was a bug in my code. Now I can successfully save & load the layout. Great!
- Regarding the API for moving a dockable: The core of my editor defines 4 docking areas - north, east, south, west - around the main window. The API allows to specify a docking area for each dockable. If there are other dockables in that area, they would usually be stacked together, but not necessarily. In any case, when I need to move a dockable to an area containing other dockables, I just stack it with one of them.
So, I guess the CGrid is not a good option for me. Maybe I need to learn what my use of CGrid does and replace it with the dockables it creates.