KDAB / KDDockWidgets

KDAB's Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets
Other
747 stars 163 forks source link

Apply layout of one set of dockwidgets to a different set #470

Open bbc131 opened 5 months ago

bbc131 commented 5 months ago

I have the following use case, for which I couldn't find a nice solution.

My starting situation is as follows, I have an arbitrary number of MainWindows (MW_0, MW_1, ...). For each of those MainWindows, there is a fixed number of DockWidgets, say for example two, which all have an affinity to this one MainWindow. For simplicity I use the uniqueName of the DockWidgets as affinity-string.

For example there could be two MainWindows MW_0 and MW_1 and four DockWidgets: DW_0_A, DW_0_B with affinity to MW_0 and DW_1_A, DW_1_B with affinity to MW_1.

Now, this is what I would like to achieve: I want to save the layout of all DockWidgets belonging to for example MW_0, which are DW_0_A, DW_0_B. And in a second step, I would like to apply this saved layout to a different group of DockWidgets, for example DW_1_A, DW_1_B.

My first idea, was to use LayoutSaver::setAffinityNames and to change the affinities of the DockWidgets temporarily. This means just before I want to save or restore their layout I change the affinities, just to change it back again afterwards.

For example, if I want to save the layout of DW_0_A and DW_0_B, I would change their affinity string from "MW_0" to "MW_x", then I would create a LayoutSaver, set its affinity also to "MW_x", save the layout and then change the affinities of DW_0_A and DW_0_B back to "MW_0" again. This doesn't work, because one cannot change the affinities of a DockWidget.

void DockWidget::setAffinities(..)
...
    if (!d->affinities.isEmpty()) {
        KDDW_ERROR("Affinity is already set, refusing to change."
                   "Submit a feature request with a good justification.");
        return;
    }
...

I don't know if this case is a good justification to allow this, you tell me ;) But in my opinion, it would be kind of a hacky solution with this changing of affinities back and forth.

I found a different workaround, which is even more ugly: I modify the uniqueNames within the serialized layout. In combination with LayoutSaver::setAffinityNames, I can "transfer" the layout of one set of DockWidgets to another.

iamsergio commented 4 months ago

I don't have a better answer than those workarounds, the use case is a bit exotic.

If you have access to DockWidget_p.h you can change d->affinities yourself, if you are aware of possible edge cases (I listed a few).

I'm tempted to allow changing affinities if the dock widget is currently closed.

bbc131 commented 4 months ago

Actually, my first idea cannot work. I cannot make the LayoutSaver save the layout of a dockwidget with unique-name "A" and restore it on another dockwidget with unique-name "B". Changing the affinites doesn't help here, forget this idea.

So currently, I have only the second idea, where I replace the unique names (and affinities) within the serialized layout. This works, I tested it. But I really don't like to rely on the fact, that I can get the layout in plain-text and find and replace unique names therein.

Generally speaking, the layout is transferred from one set of dockwidgets to another set of dockwidgets, simply using a mapping between the unique names. The mapping can be applied on the serialized plain-text layout, as already said, but it could also be done right before serialization and restoration. In any case, the responsibility that there are no conflicts etc. would lie on the user.

I think this could be a feature of the LayoutSaver. It would be a single function which sets a mapping. And if a mapping is set, right after serialization or right before restoration, the unique-names of all dock-widgets and main-windows in LayoutSaver::Layout would then be changed.

layout_saver = LayoutSaver()
# Generalize the layout; Could also use this final names if they are known at this point of time
layout_saver.use_mapping( { "MW_0"  : "MW_x", 
                            "DW_0_A": "DW_x_A", 
                            "DW_0_B": "DW_x_B" } )
generalized_layout = layout_saver.serializeLayout()

...

layout_saver = LayoutSaver()
layout_saver.use_mapping( { "MW_x"  : "MW_1", 
                            "DW_x_A": "DW_1_A", 
                            "DW_x_B": "DW_1_B" } )
layout = layout_saver.restoreLayout(generalized_layout)

This would need to be extended, to have one mapping for the unique-names and one for the affinities.