Hi Beni,
I tried to add my Dockables which are only known at Runtime in a Container. I read that CGridArea could be used like a SingleCDockable. So I used CGridArea as my container. Code works fine so far, but I have a probably loading the layout.
Here is a simple example. One time running works fine, because there is no .layout file, but running it twice causes an Excepetion.
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGridArea;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
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.SingleCDockablePerspective;
import bibliothek.util.filter.PresetFilter;
public class TestPerspective {
public static void main(String[] args) throws IOException {
new TestPerspective();
}
JFrame frame;
CControl dockControl;
File file = new File("TestPerspective.layout");
public TestPerspective() throws IOException {
// Init Frame
frame = new JFrame("Test Perspective");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(400, 300));
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
dockControl.writeXML(file);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
// Init CControl
dockControl = new CControl(frame);
frame.add(dockControl.getContentArea());
// InitFactories
dockControl.addSingleDockableFactory(new PresetFilter<String>("Grid",
"Red", "Green"), new SingleCDockableFactory() {
@Override
public SingleCDockable createBackup(String id) {
if ("Grid".equals(id)) {
CGridArea grid = dockControl.createGridArea("Grid");
grid.setTitleShown(true);
grid.setTitleText("Grid");
return grid;
}
if ("Red".equals(id)) {
return createDockable("Red", Color.RED);
}
if ("Green".equals(id)) {
return createDockable("Green", Color.GREEN);
}
return null;
}
public SingleCDockable createDockable(String title, Color color) {
JPanel background = new JPanel();
background.setOpaque(true);
background.setBackground(color);
return new DefaultSingleCDockable(title, title, background);
}
});
// InitPerspectives
// Load default saved perspectives
if (file.exists()) {
dockControl.readXML(file);
}
CControlPerspective perspecitves = dockControl.getPerspectives();
CPerspective p = perspecitves.createEmptyPerspective();
CGridPerspective gp = p.getContentArea().getCenter();
gp.gridAdd(0, 0, 1, 1, new SingleCDockablePerspective("Red"));
gp.gridAdd(1, 0, 1, 1, new SingleCDockablePerspective("Grid"));
gp.gridAdd(2, 0, 1, 1, new SingleCDockablePerspective("Green"));
gp.gridDeploy();
p.shrink();
perspecitves.setPerspective(p, true);
frame.setVisible(true);
}
}
It’s wrong usage because the factory creating “grid” registers “grid” as root-station, that is not the factories job.
On the other hand the framework throws an exception when the same station is registered twice - because “two” stations are not allowed to have the same unique identifier. I’ll update the framework to ignore the second call. It’s not a very good solution to the issue, but the only solution I see.
You may consider changing your code to:
CGridArea grid = new CGridArea(dockControl, "Grid");
grid.setTitleShown(true);
grid.setTitleText("Grid");
return grid;
}```
... this way the factory would not register the "grid".
I do not think that it is a problem that an Excption is thrown, if a station is registred twice. I think Exceptions are good. It is good to see that somethings is wrong. If you would ignore the second call, I would not have realized that the usage is wrong. If I wanted I could handle the Exception. But I am not that deep in the framework, that I could tell you which way is better. But I would think about keeping the Exception.
I changed the code and it works. Thanks.
But I do not understand, why the CGirdArea is registred twice. The factory method of the SingleCDockableFactory is not called twice. Is the Area registrated on #readXML? I think this would be a design problem.
On second thought: you are right, the exception should remain, but the message can be improved. And yes, during readXML the station is registered. The framework assumes that a factory does nothing but create the station, it does not expect the factory to register the station (usually a factory is rather dumb). So this is a feature, not a bug
I think in cases like these - when handling new items from a factory - the framework should first check whether a station or dockable is already registered, and don’t try to add it a second time. Of course collisions with two objects having the same unique identifier must always produce an exception.
But if there is no file I read the layout from, I do not call #readXML. And if the SingleCDockableFactory is changed like you suggested, the CGridArea is never registrated. Right? What is the registration for. The program seems to work.
And why is it registrated on #readXML, but not when I create the perspectives myself it is not registrated.
You are right. But the solution is still correct, because there is more than one issue here.
And why is it registrated on #readXML, but not when I create the perspectives myself it is not registrated.
Because you added a new „SingleCDockablePerspective“ for „grid“. But grid is also a station, and as a result you should be using „CGridPerspective“. It took me a while to realize this issue, but it explains why the application behaves differently when reading a file and when reading the perspective. With this change you will get an exception when reading the perspective as well.
The correct call would be: gp.gridAdd(1, 0, 1, 1, new CGridPerspective( "Grid", null ));
What is the registration for. The program seems to work.
The registration:
can prevent the framework from automatically removing the station if it has no child or only one child (the test is usually triggered after a drag and drop operation)
enhances the frameworks ability to store the location of invisible dockables. This is something you usually will only notice when you know where to look.
As you know, the framework consists of „Core“ and „Common“. Both projects have their own registration mechanism, your application works because the registration in „Core“ handles the station correctly. But the station is denied of some features of Common.
As I see the problem I have to include these fixes in the framework:
The factory will notice an attempt to re-register a station and ignore the call.
There is a new exception thrown if a station is re-registered
If the type used for the perspectives, and the object created by the SingleCDockableFactory do not match, an exception will be thrown. In your case the exception would tell you, that the type of „Grid“ is not correct.
Since your input has been highly valuable: you have any objections or better ideas?
If not, then I’ll upload the fixed version tomorrow morning (there are some other open issues).
Hi Beni,
thanks for your work. I am right now concerend with another topic, but hopefully I can concentrate on DockingFrames in the near future. I will give you my feedback then. I just wanted you to know, that I am still there.