Layout loading problems with version 1.1.0p3 and newer

Hi Beni,

from version 1.1.0p3 on I have a problem loading the layout for some DockStations. I use the Core framework and simply load the locations for the DockStations one by one into a SplitDockPathProperty from an XML file, using this property to drop the stations to their last position.
This worked fine till version 1.1.0p2. But with the changes for the next version it won’t anymore. (In my special case the stations get arranged one upon the other.)

Maybe you changed some loading code for your new perspectives?

Thanks for any help you can give.

I did, although not much. I also fixed some bugs where old data was not removed properly. Unfortunately I cannot tell you exactly which change is responsible for the problems.

Could you post a small application that reproduces the bug? Perhaps there is just some small fix required or some parameter wrong…

And in any case you might consider using a (Predefined)DockSituation to store the layout, because it stores much more information that a few SplitDockPathProperties ever could.

While trying to write an example application for you, I experienced some strange values of the XML output for some DockableProperties, like this one:


     <red>
         <node location="RIGHT" size="0.5878378378378378"  id="1294229978260"/>
         <node location="RIGHT" size="0.6608187134502923"  id="1294230065878"/>
         <node location="RIGHT" size="0.6608187134502923"  id="1294230065878"/>
         <leaf id="1294230065878"/>
     </red>
 

Should a node like this be possible at all? (twice the same id of a sub-node! [and also the same like the leaf id])

This was the most right of four stations in a line. Of course loading this produces an erroneous layout. Correcting the XML file by changing the IDs in a convenient way allows a successful loading.

That should not happen. Of course any search over the nodes fails because of these ids.

I don’t yet know exactly what happened, I’ll write back once (or if…) I find out. That could be on the weekend. In any case some code must be added to make absolutely sure that no id can appear twice (even if some SplitDockPathProperties do no longer match).

It might be possible that while inserting Dockables new nodes and leafs were created using the ids of the SplitDockPathProperty, and if there were two paths with the same ids… well, this is just a theory.

Thanks for this data, and sorry for the inconvenience.

Maybe I don’t attend to something important while loading, because also with correct XML files like this


  <?xml version='1.0'?>
  <layout>
      <red>
          <node location="RIGHT" size="0.5878378378378378"  id="1294232966380"/>
          <node location="RIGHT" size="0.6608187134502923"  id="1294233012423"/>
          <leaf id="1294233012424"/>
      </red>
      <green>
          <node location="LEFT" size="0.41216216216216217"  id="1294232966380"/>
          <node location="TOP" size="0.6334745762711864"  id="1294233071307"/>
          <leaf id="1294230065870"/>
      </green>
      <blue>
          <node location="LEFT" size="0.41216216216216217"  id="1294232966380"/>
          <node location="BOTTOM" size="0.3665254237288136"  id="1294233071307"/>
          <leaf id="1294232804505"/>
      </blue>
      <yellow>
          <node location="RIGHT" size="0.5878378378378378"  id="1294232966380"/>
          <node location="LEFT" size="0.3391812865497077"  id="1294233012423"/>
          <leaf id="1294233012422"/>
      </yellow>
  </layout>
  

I can’t restore the correct layout.
I use XML because it’s human readable. If I look on this file, I can see that ‘green’ and ‘blue’ should be above each other (‘green’ upside) and left from the rest. ‘red’ and ‘yellow’ should be on the right side and ‘red’ even right of ‘yellow’. This way it is well-defined what the layout should look like.

But if I load it with a simple code like this


             FileInputStream in = new FileInputStream("layout.xml");
             xmlLayout = XIO.readUTF(in);
             DockableProperty location = new SplitDockPathProperty();
 
             location.load(xmlLayout.getElement("red"));
             if (!root.drop(red, location))
                 root.drop(red);
 
             location.load(xmlLayout.getElement("green"));
             if (!root.drop(green, location))
                 root.drop(green);
 
             location.load(xmlLayout.getElement("blue"));
             if (!root.drop(blue, location))
                 root.drop(blue);
 
             location.load(xmlLayout.getElement("yellow"));
             if (!root.drop(yellow, location))
                 root.drop(yellow);
 

it won’t even show up the right way with the older versions. Till 1.1.0p2 I get ‘blue’ on the bottom and ‘green’, ‘yellow’ and ‘red’ in a line above ‘blue’. In 1.1.0p3 I only get all the stations in one horizontal line.

I know that the order in which the stations are dropped is important. Normally I load the station with the least sub-nodes first, than the ones with the more complex location information, up to the one with the most sub-nodes. But in this case it doesn’t make a difference, because all the leafs have two sub-nodes.
Is there any other way to distinguish the right order to load?

Running your example I saw that the algorithm in “drop” does not work correct. I could even see how ids were duplicated. I’ve corrected the algorithm a bit, but that won’t help in the long run.

After inserting the first two nodes this layout is created:

Root [id=1294246467149]
	Node[ HORIZONTAL , id=1294233071307, placeholders={}]
		Leaf[ green, placeholders={}, id=1294230065870 ]
		Leaf[ red, placeholders={}, id=1294233012424 ]

Now when the third Dockable is inserted, the id of the real node and of the second node in the path of “blue” match. The result is this layout:

Node[ VERTICAL , id=1294232966380, placeholders={}]
	Node[ HORIZONTAL , id=1294233071307, placeholders={}]
		Leaf[ green, placeholders={}, id=1294230065870 ]
		Leaf[ red, placeholders={}, id=1294233012424 ]
	Leaf[ blue, placeholders={}, id=1294232804505 ]

This may not be what you expect, and the reason for this is that “1294233071307” is a horizontal node, while in the path of “blue” the node (implicitly) is vertical. The algorithm resolves this problem by declaring the path invalid starting at “1294233071307”.
Now it would certainly be possible to modify the algorithm such that this issue would resolve correctly, but I can guarantee you with 99.9% certainty that all the repairing in the world could not end in an algorithm that solves all combinations of paths.
I have a strong argument supporting my claim: imagine a layout with many more Dockables (let’s say 100). Because of bad luck two Dockables with long but different paths are loaded first. The algorithm will put them in the same node, because there is nothing else to do. Now you have two paths, two ids, but can only store one id in the real node. The other id is lost for future runs of the algorithm. This means every time you insert a Dockable some information is lost, until there is a time when ambiguities arise and any algorithm fails.
Ordering the Dockables when inserting may help, but in the end it would be just a workaround of the bigger issue. And finding the correct order might not be easy either.

In short: your initial solution will most certainly never work properly.

I still strongly advise to use the classes “DockSituation” or “PredefinedDockSituation” to store the layout *. Both are quite easy to use and both can create XML as well. They have been used in many applications and are quite reliable today.
You just create a “DockSituation”, add factories (and/or in the case of PredefinedDocksituation: call “put(String,DockElement)” to assign unique identifiers to Dockables/DockStations), and then use “writeXML” to write the layout. If you wish I can write you an example.

  • or “DockFrontend” which offers some other nice methods and hides at least some complexity.

The only algorithm I can imagine had to store all the original layout information (with the original IDs from the XML file), which comes in piece by piece with every ‘drop(…)’ and parse it in complete every time a new piece arrives. That way the algorithm would have the complete view on the data at last, like me looking into the XML file. You could argue that is like a “global” view on the layout situation which is covered by DockSituations but I’m dealing with a more complex use case:

I have a default layout of three main stations on a root SplitDockStation (either placed by a SplitDockGrid or, till now, dropped with saved local location information). Two of which are SplitDockStations themselves and one is a StackDockStation with another two SplitDockStations as its only possible children.
Dockables can be added to any of the four resulting SplitDockStations, but only one-by-one! It is not possible to predict what dockable will when be added to what station (if any at all). Dockables can be requested for any station at any time. They can only be drag-and-dropped within their mother station. Therefore (and for automated testing reasons) these stations (and dockables) are implemented through different classes and have to be identifiable. Momentarily all stations are created manually, there are no factories. The user can also stack dockables into anonymous dynamic StackDockStations (on these mother stations) provided by the framework or drag them out to externalize them (and of course drop them back in to there mother station).
The stations from the root SplitDockStation to the resulting four SplitDockStations are “static”, i.e. only these will be there all the time.

I can only imagine to use global layout information up to the four possible mother stations.
All other layout information has to be local. Although there are some dockables which are requested most likely for every run, for which I want to provide the possibility to restore the layout too, but I can’t rely on that. This uncertainty is the reason why I chose which is called “local layout information” in the core.pdf in the first place, the DockableProperties.

I have never used the other more complex ways for layout handling. And I don’t know whether they are suitable for my use case.

I should have mentioned that dockables can also be destroyed at any time. — In this case I also would like to save their last layout (like the framework does temporarily if the user hides them).

More precise:
For layout storing I need the possibility to additionally store layout information of elements which doesn’t even exist anymore (like I could using the local DockProperties before).
For layout loading I need something that doesn’t create any dockables or stations at all (except for dynamically created StackDockStations for already existing stacked dockables on the mother stations), just adjusts the layout of the existing elements and provides layout information for later created dockables.

My first tries with a PredefinedDockSituation doesn’t work. Storing maybe works fine (at least with the residual elements which still existed at storing time). I can’t really evaluate that, because I don’t know what information is necessary for successful loading. If I understand the “PersistentLayoutExample” correctly, I have to provide a fully prepared PredefinedDockSituation for loading too. That means I need to ‘put(…)’ all DockElements with their keys into the PredefinedDockSituation before I can call ‘readXML(…)’. - But in my case this is impossible. If I load the layout at initialization time I won’t have all the elements and I also don’t want to create them, but use their layout information later, when they are dynamically requested and created.

Ok, I see this is a complex matter.

There is an interface called “PlaceholderStrategy”, with its help you could leave markers (placeholders, which are not much more than Strings) in the layout. You would need to have some algorithm converting Dockable to placeholder, but I assume that would be possible somehow. You can install a custom strategy using “DockController.getProperties().set( PlaceholderStrategy.PLACEHOLDER_STRATEGY, …)”. You then just have to make sure that you use “SplitDockPlaceholderProperty” instead of “SplitDockPathProperty”, otherwise the placeholders are ignored.

The placeholders are vastly supperior to paths because they remain in the global layout even if you remove a Dockable. Granted, you need to store the global layout as well (see text below). But afterwards you can add and remove Dockables in any order and the layout will not change.

In order to store the placeholders that remain on the DockStations you may need to utilize a DockSituation. The idea would be to remove all items of the station, and then store the station. By removing the items their placeholder gets inserted, and the placeholder is what you need later to drop Dockables again. When loading the layout the station remains empty, but with the SplitDockPlaceholderProperty you can then add the Dockables to their already existing yet invisible leafs in the tree.

You could also make use of the classes SplitDockTree, SplitDockTreeFactory and methods SplitDockStation.dropTree/visit to create and set SplitDockTrees (=the layout of a SplitDockStation). This would mean you have to manually store and load the tree, but you could modify the tree while doing so (e.g. replace Dockables by placeholders).

This may be a bit off topic and not really help your case. But I’d like just to make sure that you don’t mistake the DockSituation as un-customizeable:

  • A DockSituation allows lazy creation and using existing Dockables at the same time. The trick is simple: one Dockable appears only once in the application. So the DockSituation will call the factory for that Dockable only once. Therefore for the DockSituation it does not matter whether the item already existed, or whether the factory created a new item. (The PredefinedDockSituation fools the original algorithm with exactly this trick). So a factory could look like this:
public Dockable layout( X myLayoutInfo ){
  if( myLayoutInfo is already present ){
    return someMap.get( myLayoutInfo.someIdentifier() );
  }
  else if( myLayoutInfo is valid ){
    return new MyDockable( myLayoutInfo );
  }
  else{
    return null;
  }
}
  • DockSituation was written such that missing Dockables can be handled, if your application is not able to show some item then you just don’t load it. The only bad thing in this scenario is, that the placeholder for the item will not be loaded either.

Thanks a lot for your efforts!
But for the time being I stepped away from saving the layout of the Dockables because of a lack of time to implement the required stuff (and also to look deeper into the elements you mentioned).
Saving and loading the layout of the static stations with a PredefinedDockSituation works fine, as long as I ignore all children of the mother stations with a DockSituationIgnore.

I’ll attend to the Dockables later if I get some spare time.
After all, my current layout efforts were problem driven and the new solution works fine with the most current DockingFrames-Version (and I spare the search for the correct loading order).

Again a big Thank-you for your suggestions I will definitively try them out, but first I have other things to attend to.

That is of course your decision. Good luck with the other things to attend. Say hello if you decide to work on the Dockables again :wink: .