Add support for saving and restoring wxAuiNotebook layout

This includes multiple tab controls and pages order and uses new
wxAuiSerializer and wxAuiDeserializer notebook-specific functions.
These functions are pure virtual, which may be inconvenient if the
program doesn't use wxAuiNotebook at all or doesn't allow splitting or
reordering their pages, as it forces to still define them in this case,
but this is arguably not too burdensome and making them pure virtual
helps to attract attention to the fact that they need to be implemented
for saving notebook layouts.

Update the sample to show how to support notebook layout serialization
in the simplest possible way, by creating a separate top-level XML tag
for the notebooks. It could be tidier to put information about them
inside the corresponding pane tags, but doing it like this is simpler
and more clear, which is important for the sample code.

Closes #24950.
This commit is contained in:
Vadim Zeitlin
2024-12-16 01:54:27 +01:00
parent 93016c95e0
commit fe7df68383
6 changed files with 561 additions and 54 deletions
+8
View File
@@ -26,6 +26,9 @@
#include "wx/compositebookctrl.h"
class wxAuiSerializer;
class wxAuiDeserializer;
class wxAuiNotebook;
class wxAuiTabFrame;
@@ -432,6 +435,11 @@ public:
// Internal, don't use: use GetPagePosition() instead.
bool FindTab(wxWindow* page, wxAuiTabCtrl** ctrl, int* idx) const;
// Serialization support: this is only used by wxAuiManager, don't use
// directly.
void SaveLayout(const wxString& name, wxAuiSerializer& serializer) const;
void LoadLayout(const wxString& name, wxAuiDeserializer& deserializer);
protected:
// Common part of all ctors.
void Init();
+35
View File
@@ -16,6 +16,8 @@
// Classes used to save/load wxAuiManager layout.
// ----------------------------------------------------------------------------
// Fields common to wxAuiPaneLayoutInfo and wxAuiTabLayoutInfo containing
// information about a docked pane or tab layout.
struct wxAuiDockLayoutInfo
{
// Identifies the dock containing the pane.
@@ -33,6 +35,15 @@ struct wxAuiDockLayoutInfo
int dock_size = 0;
};
// This struct contains information about the layout of a tab control in a
// wxAuiNotebook, including where it is docked and the order of pages in it.
struct wxAuiTabLayoutInfo : wxAuiDockLayoutInfo
{
// If this vector is empty, it means that the tab control contains all
// notebook pages in natural order.
std::vector<int> pages;
};
// This struct contains the pane name and information about its layout that can
// be manipulated by the user interactively.
struct wxAuiPaneLayoutInfo : wxAuiDockLayoutInfo
@@ -87,6 +98,25 @@ public:
// Called after the last call to SavePane(), does nothing by default.
virtual void AfterSavePanes() { }
// Called before starting to save information about the notebooks, does
// nothing by default.
virtual void BeforeSaveNotebooks() { }
// Called before starting to save information about the tabs in the
// notebook in the AUI pane with the given name.
virtual void BeforeSaveNotebook(const wxString& name) = 0;
// Called to save information about a single tab control in the given
// notebook.
virtual void SaveNotebookTabControl(const wxAuiTabLayoutInfo& tab) = 0;
// Called after saving information about all the pages of the notebook in
// the AUI pane with the given name, does nothing by default.
virtual void AfterSaveNotebook() { }
// Called after the last call to SaveNotebook(), does nothing by default.
virtual void AfterSaveNotebooks() { }
// Called after saving everything, does nothing by default.
virtual void AfterSave() { }
};
@@ -116,6 +146,11 @@ public:
// Load information about all the panes previously saved with SavePane().
virtual std::vector<wxAuiPaneLayoutInfo> LoadPanes() = 0;
// For a pane containing wxAuiNotebook, load information about all the tab
// controls inside it.
virtual std::vector<wxAuiTabLayoutInfo>
LoadNotebookTabs(const wxString& name) = 0;
// Create the window to be managed by the given pane: this is called if any
// of the panes returned by LoadPanes() doesn't exist in the existing
// layout and allows to create windows on the fly.
+110 -15
View File
@@ -8,25 +8,16 @@
/////////////////////////////////////////////////////////////////////////////
/**
Description of user-modifiable pane layout information.
Description of a docked element layout.
This struct is used with wxAuiSerializer and wxAuiDeserializer to store the
pane layout. Its fields have the same meaning as the corresponding fields
in wxAuiPaneInfo (with the exception of `is_maximized`), but it doesn't
contain the fields that it wouldn't make sense to serialize.
The fields in this struct are shared by wxAuiPaneLayoutInfo and
wxAuiTabLayoutInfo and contain information about the layout of a docked
pane or tab layout.
@since 3.3.0
*/
struct wxAuiPaneLayoutInfo
*/
struct wxAuiDockLayoutInfo
{
/**
Ctor sets the name, which is always required.
*/
explicit wxAuiPaneLayoutInfo(wxString name);
/// Unique name of the pane.
wxString name;
/// Direction of the dock containing the pane.
int dock_direction = wxAUI_DOCK_LEFT;
@@ -44,7 +35,47 @@ struct wxAuiPaneLayoutInfo
/// Size of the containing dock.
int dock_size = 0;
};
/**
Contains information about the layout of a tab control in a wxAuiNotebook.
This includes where it is docked, via the fields inherited from
wxAuiDockLayoutInfo, and the order of pages in it.
@since 3.3.0
*/
struct wxAuiTabLayoutInfo : wxAuiDockLayoutInfo
{
/**
Indices of the pages in this tab control in their order on screen.
If this vector is empty, it means that the tab control contains all
notebook pages in natural order.
*/
std::vector<int> pages;
};
/**
Description of user-modifiable pane layout information.
This struct is used with wxAuiSerializer and wxAuiDeserializer to store the
pane layout. Its fields, including the inherited ones from
wxAuiDockLayoutInfo, have the same meaning as the corresponding fields in
wxAuiPaneInfo (with the exception of `is_maximized`), but it doesn't
contain the fields that it wouldn't make sense to serialize.
@since 3.3.0
*/
struct wxAuiPaneLayoutInfo : wxAuiDockLayoutInfo
{
/**
Ctor sets the name, which is always required.
*/
explicit wxAuiPaneLayoutInfo(wxString name);
/// Unique name of the pane.
wxString name;
/// Position of the pane when floating, may be invalid.
wxPoint floating_pos = wxDefaultPosition;
@@ -126,6 +157,57 @@ public:
*/
virtual void AfterSavePanes();
/**
Called before starting to save information about the notebooks.
Does nothing by default.
Note that this function is called after AfterSavePanes() but may not be
called at all if there are no panes containing wxAuiNotebook.
*/
virtual void BeforeSaveNotebooks();
/**
Called before starting to save information about the tabs in the
notebook in the AUI pane with the given name.
This function needs to be overridden to keep record of the notebook for
which SaveNotebookTabControl() will be called next. Of course, if
saving notebook layout is unnecessary, e.g. because the program doesn't
use wxAuiNotebook at all, the implementation can be trivial and just do
nothing.
This function is called one or more times after BeforeSaveNotebooks().
*/
virtual void BeforeSaveNotebook(const wxString& name) = 0;
/**
Called to save information about a single tab control in the given
notebook.
This function will be called for all tab controls in the notebook after
BeforeSaveNotebook().
As with that function, it has to be implemented, but can simply do
nothing if saving notebook layout is not necessary.
*/
virtual void SaveNotebookTabControl(const wxAuiTabLayoutInfo& tab) = 0;
/**
Called after saving information about all the pages of the notebook in
the AUI pane with the given name.
Does nothing by default.
*/
virtual void AfterSaveNotebook();
/**
Called after the last call to SaveNotebook().
Does nothing by default.
*/
virtual void AfterSaveNotebooks();
/**
Called after saving everything.
@@ -190,6 +272,19 @@ public:
*/
virtual std::vector<wxAuiPaneInfo> LoadPanes() = 0;
/**
Load information about the notebook tabs previously saved by
wxAuiSerializer::SaveNotebookTabControl().
The pane with the name @a name is guaranteed to exist in the layout and
have wxAuiNotebook as the associated window.
If restoring the notebook layout is not necessary, this function can
just return an empty vector.
*/
virtual std::vector<wxAuiTabLayoutInfo>
LoadNotebookTabs(const wxString& name) = 0;
/**
Create the window to be managed by the given pane if necessary.
+177 -34
View File
File diff suppressed because it is too large Load Diff
+195
View File
File diff suppressed because it is too large Load Diff
+36 -5
View File
@@ -25,6 +25,7 @@
#include "wx/aui/floatpane.h"
#include "wx/aui/tabmdi.h"
#include "wx/aui/auibar.h"
#include "wx/aui/auibook.h"
#include "wx/aui/serializer.h"
#include "wx/mdi.h"
#include "wx/wupdlock.h"
@@ -69,6 +70,7 @@ wxDEFINE_EVENT( wxEVT_AUI_FIND_MANAGER, wxAuiManagerEvent );
#include "wx/generic/private/drawresize.h"
#include <map>
#include <memory>
wxIMPLEMENT_DYNAMIC_CLASS(wxAuiManagerEvent, wxEvent);
@@ -1451,6 +1453,10 @@ void wxAuiManager::SaveLayout(wxAuiSerializer& serializer) const
{
serializer.BeforeSavePanes();
// Collect information about all the notebooks we may have while saving
// the panes layout.
std::map<wxString, wxAuiNotebook*> notebooks;
for ( const auto& pane : m_panes )
{
wxAuiPaneLayoutInfo layoutInfo{pane.name};
@@ -1460,9 +1466,26 @@ void wxAuiManager::SaveLayout(wxAuiSerializer& serializer) const
MakeDIP(m_frame, layoutInfo.floating_size);
serializer.SavePane(layoutInfo);
if ( auto* const nb = wxDynamicCast(pane.window, wxAuiNotebook) )
{
notebooks[pane.name] = nb;
}
}
serializer.AfterSavePanes();
if ( !notebooks.empty() )
{
serializer.BeforeSaveNotebooks();
for ( const auto& kv : notebooks )
{
kv.second->SaveLayout(kv.first, serializer);
}
serializer.AfterSaveNotebooks();
}
}
serializer.AfterSave();
@@ -1504,7 +1527,7 @@ void wxAuiManager::LoadLayout(wxAuiDeserializer& deserializer)
hasMaximized = true;
// Find the pane with the same name in the existing layout.
bool found = false;
wxWindow* window = nullptr;
for ( auto& existingPane : panes )
{
if ( existingPane.name == layoutInfo.name )
@@ -1512,21 +1535,29 @@ void wxAuiManager::LoadLayout(wxAuiDeserializer& deserializer)
// Update the existing pane with the restored layout.
CopyLayoutTo(layoutInfo, existingPane);
found = true;
window = existingPane.window;
break;
}
}
// This pane couldn't be found in the existing layout, let deserializer
// create a new window for it if desired, otherwise just ignore it.
if ( !found )
if ( !window )
{
wxAuiPaneInfo pane;
pane.name = layoutInfo.name;
CopyLayoutTo(layoutInfo, pane);
if ( const auto w = deserializer.CreatePaneWindow(pane) )
newPanes.emplace_back(w, pane);
window = deserializer.CreatePaneWindow(pane);
if ( !window )
continue;
newPanes.emplace_back(window, pane);
}
if ( auto* const nb = wxDynamicCast(window, wxAuiNotebook) )
{
nb->LoadLayout(layoutInfo.name, deserializer);
}
}