mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-08-20 22:06:25 +08:00
Merge branch 'aui-serialize'
Provide a way to store AUI layouts in any custom format. Closes #24225. See #24235.
This commit is contained in:
@@ -4018,6 +4018,7 @@ COND_USE_GUI_1_ALL_GUI_HEADERS = \
|
||||
wx/aui/tabart.h \
|
||||
wx/xrc/xh_aui.h \
|
||||
wx/xrc/xh_auitoolb.h \
|
||||
wx/aui/serializer.h \
|
||||
$(AUI_GTK_HDR) \
|
||||
$(AUI_PLATFORM_HDR) \
|
||||
wx/propgrid/advprops.h \
|
||||
|
||||
@@ -3006,6 +3006,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
|
||||
wx/aui/tabart.h
|
||||
wx/xrc/xh_aui.h
|
||||
wx/xrc/xh_auitoolb.h
|
||||
wx/aui/serializer.h
|
||||
</set>
|
||||
<set var="AUI_HDR" hints="files">
|
||||
$(AUI_CMN_HDR)
|
||||
|
||||
@@ -2793,6 +2793,7 @@ set(AUI_CMN_HDR
|
||||
wx/aui/tabart.h
|
||||
wx/xrc/xh_aui.h
|
||||
wx/xrc/xh_auitoolb.h
|
||||
wx/aui/serializer.h
|
||||
)
|
||||
|
||||
set(AUI_MSW_HDR
|
||||
|
||||
@@ -11,7 +11,7 @@ wx_add_sample(access accesstest.cpp DEPENDS wxUSE_ACCESSIBILITY)
|
||||
wx_add_sample(animate anitest.cpp anitest.h DATA throbber.gif throbber_2x.gif hourglass.ani DEPENDS wxUSE_ANIMATIONCTRL)
|
||||
wx_add_sample(archive CONSOLE)
|
||||
wx_add_sample(artprov arttest.cpp artbrows.cpp artbrows.h)
|
||||
wx_add_sample(aui auidemo.cpp LIBRARIES wxaui wxhtml NAME auidemo DEPENDS wxUSE_AUI)
|
||||
wx_add_sample(aui auidemo.cpp LIBRARIES wxaui wxhtml wxxml NAME auidemo DEPENDS wxUSE_AUI)
|
||||
wx_add_sample(calendar DEPENDS wxUSE_CALENDARCTRL)
|
||||
wx_add_sample(caret DEPENDS wxUSE_CARET)
|
||||
wx_add_sample(collpane DEPENDS wxUSE_COLLPANE)
|
||||
|
||||
@@ -2722,6 +2722,7 @@ AUI_CMN_HDR =
|
||||
wx/aui/auibar.h
|
||||
wx/aui/tabmdi.h
|
||||
wx/aui/aui.h
|
||||
wx/aui/serializer.h
|
||||
wx/aui/tabart.h
|
||||
wx/xrc/xh_aui.h
|
||||
wx/xrc/xh_auitoolb.h
|
||||
|
||||
@@ -931,6 +931,7 @@
|
||||
<ClInclude Include="..\..\include\wx\aui\tabartmsw.h" />
|
||||
<ClInclude Include="..\..\include\wx\aui\barartmsw.h" />
|
||||
<ClInclude Include="..\..\include\wx\xrc\xh_aui.h" />
|
||||
<ClInclude Include="..\..\include\wx\aui\serializer.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
<ClInclude Include="..\..\include\wx\aui\framemanager.h">
|
||||
<Filter>Common Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\wx\aui\serializer.h">
|
||||
<Filter>Common Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\wx\aui\tabart.h">
|
||||
<Filter>Common Headers</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -128,6 +128,8 @@ class wxAuiPaneInfo;
|
||||
class wxAuiDockInfo;
|
||||
class wxAuiDockArt;
|
||||
class wxAuiManagerEvent;
|
||||
class wxAuiSerializer;
|
||||
class wxAuiDeserializer;
|
||||
|
||||
using wxAuiDockUIPartArray = wxBaseArray<wxAuiDockUIPart>;
|
||||
using wxAuiDockInfoArray = wxBaseArray<wxAuiDockInfo>;
|
||||
@@ -452,6 +454,12 @@ public:
|
||||
|
||||
void Update();
|
||||
|
||||
// Serialize or restore the whole layout using the provided serializer.
|
||||
void SaveLayout(wxAuiSerializer& serializer) const;
|
||||
void LoadLayout(wxAuiDeserializer& deserializer);
|
||||
|
||||
// Older functions using bespoke text format, prefer using the ones using
|
||||
// wxAuiSerializer and wxAuiDeserializer above instead in the new code.
|
||||
wxString SavePaneInfo(const wxAuiPaneInfo& pane);
|
||||
void LoadPaneInfo(wxString panePart, wxAuiPaneInfo &pane);
|
||||
wxString SavePerspective();
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/aui/serializer.h
|
||||
// Purpose: Declaration of wxAuiSerializer and wxAuiDeserializer classes.
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2024-01-20
|
||||
// Copyright: (c) 2024 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_AUI_SERIALIZER_H_
|
||||
#define _WX_AUI_SERIALIZER_H_
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Classes used to save/load wxAuiManager layout.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// wxAuiSerializer is used with wxAuiManager::SaveLayout().
|
||||
//
|
||||
// This is an abstract base class, you need to inherit from it and override its
|
||||
// pure virtual functions in your derived class.
|
||||
//
|
||||
// If any of the functions of the derived class throw an exception, it is
|
||||
// propagated out of wxAuiManager::SaveLayout() and it's callers responsibility
|
||||
// to handle it.
|
||||
class wxAuiSerializer
|
||||
{
|
||||
public:
|
||||
// Trivial default ctor.
|
||||
wxAuiSerializer() = default;
|
||||
|
||||
// Trivial but virtual dtor for a base class.
|
||||
virtual ~wxAuiSerializer() = default;
|
||||
|
||||
|
||||
// Called before doing anything else, does nothing by default.
|
||||
virtual void BeforeSave() { }
|
||||
|
||||
// Called before starting to save information about the panes, does nothing
|
||||
// by default.
|
||||
virtual void BeforeSavePanes() { }
|
||||
|
||||
// Save information about the given pane.
|
||||
virtual void SavePane(const wxAuiPaneInfo& pane) = 0;
|
||||
|
||||
// Called after the last call to SavePane(), does nothing by default.
|
||||
virtual void AfterSavePanes() { }
|
||||
|
||||
// Called before starting to save information about the docks, does nothing
|
||||
// by default.
|
||||
virtual void BeforeSaveDocks() { }
|
||||
|
||||
// Save information about the given dock.
|
||||
virtual void SaveDock(const wxAuiDockInfo& dock) = 0;
|
||||
|
||||
// Called after the last call to SaveDock(), does nothing by default.
|
||||
virtual void AfterSaveDocks() { }
|
||||
|
||||
// Called after saving everything, does nothing by default.
|
||||
virtual void AfterSave() { }
|
||||
};
|
||||
|
||||
// wxAuiDeserializer is used with wxAuiManager::LoadLayout().
|
||||
//
|
||||
// As wxAuiSerializer, this is an abstract base class, you need to inherit from
|
||||
// it and override its pure virtual functions in your derived class.
|
||||
//
|
||||
// Derived class function also may throw and, if any of them other than
|
||||
// AfterLoad() does, the existing layout is not changed, i.e.
|
||||
// wxAuiManager::LoadLayout() is exception-safe.
|
||||
class wxAuiDeserializer
|
||||
{
|
||||
public:
|
||||
// Ctor takes the manager for which we're restoring the layout, it must
|
||||
// remain valid for the lifetime of this object.
|
||||
explicit wxAuiDeserializer(wxAuiManager& manager) : m_manager(manager) { }
|
||||
|
||||
// Trivial but virtual dtor for a base class.
|
||||
virtual ~wxAuiDeserializer() = default;
|
||||
|
||||
|
||||
// Called before doing anything else, does nothing by default.
|
||||
virtual void BeforeLoad() { }
|
||||
|
||||
// Load information about all the panes previously saved with SavePane().
|
||||
virtual std::vector<wxAuiPaneInfo> LoadPanes() = 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.
|
||||
//
|
||||
// If this function returns nullptr, the pane is not added to the manager.
|
||||
virtual wxWindow* CreatePaneWindow(const wxAuiPaneInfo& WXUNUSED(pane))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Load information about all the docks previously saved with SaveDock().
|
||||
virtual std::vector<wxAuiDockInfo> LoadDocks() = 0;
|
||||
|
||||
// Called after restoring everything, calls Update() on the manager by
|
||||
// default.
|
||||
virtual void AfterLoad() { m_manager.Update(); }
|
||||
|
||||
protected:
|
||||
// The manager for which we're restoring the layout.
|
||||
wxAuiManager& m_manager;
|
||||
};
|
||||
|
||||
#endif // _WX_AUI_SERIALIZER_H_
|
||||
@@ -371,6 +371,19 @@ public:
|
||||
const wxAuiPaneInfo& insert_location,
|
||||
int insert_level = wxAUI_INSERT_PANE);
|
||||
|
||||
/**
|
||||
Load the layout information saved by SaveLayout().
|
||||
|
||||
The implementation of wxAuiDeserializer object passed to this function
|
||||
should be consistent with that of the serializer used to save the
|
||||
layout. See @ref page_samples_aui for an example of using serializer
|
||||
saving the layout in XML format and matching deserializer restoring the
|
||||
layout from it.
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
void LoadLayout(wxAuiDeserializer& deserializer) const;
|
||||
|
||||
/**
|
||||
LoadPaneInfo() is similar to LoadPerspective, with the exception that it
|
||||
only loads information about a single pane.
|
||||
@@ -389,6 +402,10 @@ public:
|
||||
/**
|
||||
Loads a saved perspective.
|
||||
|
||||
This function is used to load layouts previously saved with
|
||||
SavePerspective(), use LoadLayout() to load a layout saved with
|
||||
SaveLayout().
|
||||
|
||||
A perspective is the layout state of an AUI managed window.
|
||||
|
||||
All currently existing panes that have an object in "perspective"
|
||||
@@ -422,6 +439,19 @@ public:
|
||||
*/
|
||||
void RestoreMaximizedPane();
|
||||
|
||||
/**
|
||||
Save the layout information using the provided object.
|
||||
|
||||
This function allows to use a custom @a serializer to save the layout
|
||||
information in any format, e.g. @ref page_samples_aui shows how to save
|
||||
it in XML format.
|
||||
|
||||
See wxAuiSerializer documentation for more details.
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
void SaveLayout(wxAuiSerializer& serializer) const;
|
||||
|
||||
/**
|
||||
SavePaneInfo() is similar to SavePerspective, with the exception that it only
|
||||
saves information about a single pane.
|
||||
@@ -441,6 +471,9 @@ public:
|
||||
Saves the entire user interface layout into an encoded wxString, which
|
||||
can then be stored by the application (probably using wxConfig).
|
||||
|
||||
@note You may prefer to use SaveLayout() instead of this function for
|
||||
more flexibility.
|
||||
|
||||
@see LoadPerspective
|
||||
@see LoadPaneInfo
|
||||
@see SavePaneInfo
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: aui/serializer.h
|
||||
// Purpose: Documentation of wxAuiSerializer and wxAuiDeserializer.
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2024-02-20
|
||||
// Copyright: (c) 2024 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@class wxAuiSerializer
|
||||
|
||||
wxAuiSerializer is used by wxAuiManager::SaveLayout() to store layout
|
||||
information.
|
||||
|
||||
This is an abstract base class, you need to inherit from it and override its
|
||||
pure virtual functions in your derived class.
|
||||
|
||||
SavePane() and SaveDock() must be overridden and will be called by
|
||||
wxAuiManager for each pane and dock present in the layout. The other
|
||||
functions don't need to be overridden, but it is often convenient to
|
||||
perform some actions before or after starting to save the objects of the
|
||||
given type or at the beginning or end of the whole saving process, so this
|
||||
class provides hooks for doing it.
|
||||
|
||||
If any of the functions of the derived class throw an exception, it is
|
||||
propagated out of wxAuiManager::SaveLayout() and it's callers responsibility
|
||||
to handle it.
|
||||
|
||||
@library{wxaui}
|
||||
@category{aui}
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
class wxAuiSerializer
|
||||
{
|
||||
public:
|
||||
/// Trivial default ctor.
|
||||
wxAuiSerializer() = default;
|
||||
|
||||
/// Trivial but virtual destructor.
|
||||
virtual ~wxAuiSerializer() = default;
|
||||
|
||||
/**
|
||||
Called before doing anything else.
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void BeforeSave();
|
||||
|
||||
/**
|
||||
Called before starting to save information about the panes.
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void BeforeSavePanes();
|
||||
|
||||
/**
|
||||
Save information about the given pane.
|
||||
|
||||
This function will be called for all panes and must be implemented to
|
||||
save their data in a format from which it can be restored later using a
|
||||
matching wxAuiDeserializer implementation.
|
||||
|
||||
Note that all sizes and positions in @a pane are using DIPs, i.e.
|
||||
resolution-independent pixels, when it is passed to this function, so
|
||||
it does _not_ need to perform any scaling itself to ensure that the
|
||||
stored values are restored correctly if the resolution changes.
|
||||
*/
|
||||
virtual void SavePane(const wxAuiPaneInfo& pane) = 0;
|
||||
|
||||
/**
|
||||
Called after the last call to SavePane().
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void AfterSavePanes();
|
||||
|
||||
/**
|
||||
Called before starting to save information about the docks.
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void BeforeSaveDocks();
|
||||
|
||||
/**
|
||||
Save information about the given dock.
|
||||
|
||||
This function will be called for all docks and must be implemented to
|
||||
save their data in a format from which it can be restored later using a
|
||||
matching wxAuiDeserializer implementation.
|
||||
|
||||
As with SavePane(), the coordinates in @a dock are always in DIPs and
|
||||
this function does _not_ need to perform any scaling itself.
|
||||
*/
|
||||
virtual void SaveDock(const wxAuiDockInfo& dock) = 0;
|
||||
|
||||
/**
|
||||
Called after the last call to SaveDock().
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void AfterSaveDocks();
|
||||
|
||||
/**
|
||||
Called after saving everything.
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void AfterSave();
|
||||
};
|
||||
|
||||
/**
|
||||
@class wxAuiDeserializer
|
||||
|
||||
wxAuiDeserializer is used by wxAuiManager::LoadLayout() to restore layout
|
||||
information saved by wxAuiManager::SaveLayout().
|
||||
|
||||
As wxAuiSerializer, this is an abstract base class, you need to inherit from
|
||||
it and override its pure virtual functions in your derived class.
|
||||
|
||||
Derived class function also may throw and, if any of them other than
|
||||
AfterLoad() does, the existing layout is not changed, i.e.
|
||||
wxAuiManager::LoadLayout() is exception-safe.
|
||||
|
||||
@library{wxaui}
|
||||
@category{aui}
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
class wxAuiDeserializer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Constructor taking the manager for which we're restoring the layout.
|
||||
|
||||
The manager remains valid for the lifetime of this object.
|
||||
*/
|
||||
explicit wxAuiDeserializer(wxAuiManager& manager);
|
||||
|
||||
/// Trivial but virtual destructor.
|
||||
virtual ~wxAuiDeserializer() = default;
|
||||
|
||||
|
||||
/**
|
||||
Called before doing anything else.
|
||||
|
||||
Does nothing by default.
|
||||
*/
|
||||
virtual void BeforeLoad();
|
||||
|
||||
/**
|
||||
Load information about all the panes previously saved by
|
||||
wxAuiSerializer::SavePane().
|
||||
|
||||
Unlike the serializer function, this one is called only once and should
|
||||
return all the panes in the layout.
|
||||
|
||||
Just as the serializer function, this one doesn't need to perform any
|
||||
scaling itself as this will be done, if necessary, by wxAuiManager
|
||||
itself.
|
||||
|
||||
If some pane in the returned vector doesn't already exist, i.e. there
|
||||
is no pane with the matching name, CreatePaneWindow() is called to
|
||||
allow creating it on the fly.
|
||||
*/
|
||||
virtual std::vector<wxAuiPaneInfo> LoadPanes() = 0;
|
||||
|
||||
/**
|
||||
Create the window to be managed by the given pane if necessary.
|
||||
|
||||
This function 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.
|
||||
|
||||
If this function returns @NULL, as it does by default, the pane is not
|
||||
added to the manager.
|
||||
*/
|
||||
virtual wxWindow* CreatePaneWindow(const wxAuiPaneInfo& pane);
|
||||
|
||||
/**
|
||||
Load information about all the docks previously saved with SaveDock().
|
||||
|
||||
As with LoadPanes(), this function doesn't need to perform any scaling
|
||||
itself.
|
||||
*/
|
||||
virtual std::vector<wxAuiDockInfo> LoadDocks() = 0;
|
||||
|
||||
/**
|
||||
Called after restoring everything.
|
||||
|
||||
Default implementation calls wxAuiManager::Update(). Override this
|
||||
function and do _not_ call the base class version if you want to
|
||||
prevent this from happening, e.g. if you need to make further changes
|
||||
to the restored layout before updating it.
|
||||
*/
|
||||
virtual void AfterLoad();
|
||||
|
||||
protected:
|
||||
/// The manager for which we're restoring the layout.
|
||||
wxAuiManager& m_manager;
|
||||
};
|
||||
+386
-9
@@ -28,6 +28,7 @@
|
||||
#include "wx/log.h"
|
||||
#include "wx/menu.h"
|
||||
#include "wx/toolbar.h"
|
||||
#include "wx/sstream.h"
|
||||
#include "wx/statusbr.h"
|
||||
#include "wx/msgdlg.h"
|
||||
#include "wx/textdlg.h"
|
||||
@@ -35,6 +36,10 @@
|
||||
#include "wx/checkbox.h"
|
||||
|
||||
#include "wx/aui/aui.h"
|
||||
#include "wx/aui/serializer.h"
|
||||
|
||||
#include "wx/xml/xml.h"
|
||||
|
||||
#include "../sample.xpm"
|
||||
|
||||
// -- application --
|
||||
@@ -70,7 +75,8 @@ class MyFrame : public wxFrame
|
||||
ID_NotebookContent,
|
||||
ID_SizeReportContent,
|
||||
ID_CreatePerspective,
|
||||
ID_CopyPerspectiveCode,
|
||||
ID_CopyLayout,
|
||||
ID_PasteLayout,
|
||||
ID_AllowFloating,
|
||||
ID_AllowActivePane,
|
||||
ID_TransparentHint,
|
||||
@@ -149,7 +155,8 @@ private:
|
||||
void OnChangeContentPane(wxCommandEvent& evt);
|
||||
void OnDropDownToolbarItem(wxAuiToolBarEvent& evt);
|
||||
void OnCreatePerspective(wxCommandEvent& evt);
|
||||
void OnCopyPerspectiveCode(wxCommandEvent& evt);
|
||||
void OnCopyLayout(wxCommandEvent& evt);
|
||||
void OnPasteLayout(wxCommandEvent& evt);
|
||||
void OnRestorePerspective(wxCommandEvent& evt);
|
||||
void OnSettings(wxCommandEvent& evt);
|
||||
void OnCustomizeToolbar(wxCommandEvent& evt);
|
||||
@@ -602,7 +609,8 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
||||
EVT_MENU(MyFrame::ID_CreateSizeReport, MyFrame::OnCreateSizeReport)
|
||||
EVT_MENU(MyFrame::ID_CreateNotebook, MyFrame::OnCreateNotebook)
|
||||
EVT_MENU(MyFrame::ID_CreatePerspective, MyFrame::OnCreatePerspective)
|
||||
EVT_MENU(MyFrame::ID_CopyPerspectiveCode, MyFrame::OnCopyPerspectiveCode)
|
||||
EVT_MENU(MyFrame::ID_CopyLayout, MyFrame::OnCopyLayout)
|
||||
EVT_MENU(MyFrame::ID_PasteLayout, MyFrame::OnPasteLayout)
|
||||
EVT_MENU(ID_AllowFloating, MyFrame::OnManagerFlag)
|
||||
EVT_MENU(ID_TransparentHint, MyFrame::OnManagerFlag)
|
||||
EVT_MENU(ID_VenetianBlindsHint, MyFrame::OnManagerFlag)
|
||||
@@ -764,7 +772,8 @@ MyFrame::MyFrame(wxWindow* parent,
|
||||
|
||||
m_perspectives_menu = new wxMenu;
|
||||
m_perspectives_menu->Append(ID_CreatePerspective, _("Create Perspective"));
|
||||
m_perspectives_menu->Append(ID_CopyPerspectiveCode, _("Copy Perspective Data To Clipboard"));
|
||||
m_perspectives_menu->Append(ID_CopyLayout, _("Copy Layout to Clipboard as XML\tCtrl-C"));
|
||||
m_perspectives_menu->Append(ID_PasteLayout, _("Paste XML Layout from Clipboard\tCtrl-V"));
|
||||
m_perspectives_menu->AppendSeparator();
|
||||
m_perspectives_menu->Append(ID_FirstPerspective+0, _("Default Startup"));
|
||||
m_perspectives_menu->Append(ID_FirstPerspective+1, _("All Panes"));
|
||||
@@ -1452,16 +1461,384 @@ void MyFrame::OnCreatePerspective(wxCommandEvent& WXUNUSED(event))
|
||||
m_perspectives.Add(m_mgr.SavePerspective());
|
||||
}
|
||||
|
||||
void MyFrame::OnCopyPerspectiveCode(wxCommandEvent& WXUNUSED(evt))
|
||||
// Sample serializer and deserializer implementations for saving and loading layouts.
|
||||
class MyXmlSerializer : public wxAuiSerializer
|
||||
{
|
||||
wxString s = m_mgr.SavePerspective();
|
||||
public:
|
||||
MyXmlSerializer() = default;
|
||||
|
||||
wxString GetXML() const
|
||||
{
|
||||
wxStringOutputStream oss;
|
||||
if ( !m_doc.Save(oss) )
|
||||
{
|
||||
wxLogError("Failed to save XML document.");
|
||||
}
|
||||
|
||||
return oss.GetString();
|
||||
}
|
||||
|
||||
// Implement wxAuiSerializer methods.
|
||||
virtual void BeforeSave() override
|
||||
{
|
||||
m_root = new wxXmlNode(wxXML_ELEMENT_NODE, "aui-layout");
|
||||
m_root->AddAttribute("version", "3.3.0");
|
||||
|
||||
m_doc.SetRoot(m_root);
|
||||
}
|
||||
|
||||
virtual void BeforeSavePanes() override
|
||||
{
|
||||
m_panes = new wxXmlNode(wxXML_ELEMENT_NODE, "panes");
|
||||
}
|
||||
|
||||
virtual void SavePane(const wxAuiPaneInfo& pane) override
|
||||
{
|
||||
auto node = new wxXmlNode(m_panes, wxXML_ELEMENT_NODE, "pane");
|
||||
node->AddAttribute("name", pane.name);
|
||||
|
||||
AddChild(node, "caption", pane.caption);
|
||||
AddChild(node, "state", pane.state);
|
||||
AddChild(node, "direction", pane.dock_direction);
|
||||
AddChild(node, "layer", pane.dock_layer);
|
||||
AddChild(node, "row", pane.dock_row);
|
||||
AddChild(node, "position", pane.dock_pos);
|
||||
AddChild(node, "proportion", pane.dock_proportion);
|
||||
AddChild(node, "best-size", pane.best_size);
|
||||
AddChild(node, "min-size", pane.min_size);
|
||||
AddChild(node, "max-size", pane.max_size);
|
||||
AddChild(node, "floating-rect",
|
||||
wxRect(pane.floating_pos, pane.floating_size));
|
||||
}
|
||||
|
||||
virtual void AfterSavePanes() override
|
||||
{
|
||||
m_root->AddChild(m_panes);
|
||||
}
|
||||
|
||||
virtual void BeforeSaveDocks() override
|
||||
{
|
||||
m_docks = new wxXmlNode(wxXML_ELEMENT_NODE, "docks");
|
||||
}
|
||||
|
||||
virtual void SaveDock(const wxAuiDockInfo& dock) override
|
||||
{
|
||||
auto node = new wxXmlNode(m_docks, wxXML_ELEMENT_NODE, "dock");
|
||||
node->AddAttribute("resizable", dock.resizable ? "1" : "0");
|
||||
|
||||
AddChild(node, "direction", dock.dock_direction);
|
||||
AddChild(node, "layer", dock.dock_layer);
|
||||
AddChild(node, "row", dock.dock_row);
|
||||
AddChild(node, "size", dock.size);
|
||||
if ( dock.min_size )
|
||||
AddChild(node, "min-size", dock.min_size);
|
||||
}
|
||||
|
||||
virtual void AfterSaveDocks() override
|
||||
{
|
||||
m_root->AddChild(m_docks);
|
||||
}
|
||||
|
||||
virtual void AfterSave() override {}
|
||||
|
||||
private:
|
||||
void AddChild(wxXmlNode* parent, const wxString& name, const wxString& value)
|
||||
{
|
||||
auto node = new wxXmlNode(parent, wxXML_ELEMENT_NODE, name);
|
||||
node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, {}, value));
|
||||
}
|
||||
|
||||
void AddChild(wxXmlNode* parent, const wxString& name, int value)
|
||||
{
|
||||
AddChild(parent, name, wxString::Format("%u", value));
|
||||
}
|
||||
|
||||
void AddChild(wxXmlNode* parent, const wxString& name, const wxSize& size)
|
||||
{
|
||||
if ( size != wxDefaultSize )
|
||||
AddChild(parent, name, wxString::Format("%dx%d", size.x, size.y));
|
||||
}
|
||||
|
||||
void AddChild(wxXmlNode* parent, const wxString& name, const wxRect& rect)
|
||||
{
|
||||
if ( rect.GetPosition() != wxDefaultPosition ||
|
||||
rect.GetSize() != wxDefaultSize )
|
||||
{
|
||||
AddChild(parent, name,
|
||||
wxString::Format("%d,%d %dx%d",
|
||||
rect.x, rect.y, rect.width, rect.height));
|
||||
}
|
||||
}
|
||||
|
||||
wxXmlDocument m_doc;
|
||||
wxXmlNode* m_root = nullptr;
|
||||
wxXmlNode* m_panes = nullptr;
|
||||
wxXmlNode* m_docks = nullptr;
|
||||
};
|
||||
|
||||
class MyXmlDeserializer : public wxAuiDeserializer
|
||||
{
|
||||
public:
|
||||
explicit MyXmlDeserializer(wxAuiManager& manager, const wxString& xml)
|
||||
: wxAuiDeserializer(manager)
|
||||
{
|
||||
wxStringInputStream iss(xml);
|
||||
if ( !m_doc.Load(iss) )
|
||||
throw std::runtime_error("Failed to load XML document.");
|
||||
|
||||
const auto root = m_doc.GetDocumentNode();
|
||||
const auto layout = root->GetChildren();
|
||||
if ( !layout )
|
||||
throw std::runtime_error("Missing layout node");
|
||||
if ( layout->GetName() != "aui-layout" )
|
||||
throw std::runtime_error("Unexpected XML node name");
|
||||
if ( layout->GetAttribute("version") != "3.3.0" )
|
||||
throw std::runtime_error("Unexpected XML version");
|
||||
if ( layout->GetNext() )
|
||||
throw std::runtime_error("Unexpected multiple layout nodes");
|
||||
|
||||
for ( wxXmlNode* node = layout->GetChildren(); node; node = node->GetNext() )
|
||||
{
|
||||
if ( node->GetName() == "panes" )
|
||||
{
|
||||
if ( m_panes )
|
||||
throw std::runtime_error("Unexpected multiple panes nodes");
|
||||
|
||||
m_panes = node;
|
||||
}
|
||||
else if ( node->GetName() == "docks" )
|
||||
{
|
||||
if ( m_docks )
|
||||
throw std::runtime_error("Unexpected multiple docks nodes");
|
||||
|
||||
m_docks = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unexpected node name");
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_panes )
|
||||
throw std::runtime_error("Missing panes node");
|
||||
|
||||
if ( !m_docks )
|
||||
throw std::runtime_error("Missing docks node");
|
||||
}
|
||||
|
||||
// Implement wxAuiDeserializer methods.
|
||||
virtual std::vector<wxAuiPaneInfo> LoadPanes() override
|
||||
{
|
||||
std::vector<wxAuiPaneInfo> panes;
|
||||
|
||||
for ( wxXmlNode* node = m_panes->GetChildren(); node; node = node->GetNext() )
|
||||
{
|
||||
if ( node->GetName() != "pane" )
|
||||
throw std::runtime_error("Unexpected pane node name");
|
||||
|
||||
{
|
||||
wxAuiPaneInfo pane;
|
||||
pane.name = node->GetAttribute("name");
|
||||
|
||||
for ( wxXmlNode* child = node->GetChildren(); child; child = child->GetNext() )
|
||||
{
|
||||
const wxString& name = child->GetName();
|
||||
const wxString& content = child->GetNodeContent();
|
||||
|
||||
if ( name == "caption" )
|
||||
{
|
||||
pane.caption = content;
|
||||
}
|
||||
else if ( name == "state" )
|
||||
{
|
||||
pane.state = wxAuiPaneInfo::wxAuiPaneState(GetInt(content));
|
||||
}
|
||||
else if ( name == "direction" )
|
||||
{
|
||||
pane.dock_direction = GetInt(content);
|
||||
}
|
||||
else if ( name == "layer" )
|
||||
{
|
||||
pane.dock_layer = GetInt(content);
|
||||
}
|
||||
else if ( name == "row" )
|
||||
{
|
||||
pane.dock_row = GetInt(content);
|
||||
}
|
||||
else if ( name == "position" )
|
||||
{
|
||||
pane.dock_pos = GetInt(content);
|
||||
}
|
||||
else if ( name == "proportion" )
|
||||
{
|
||||
pane.dock_proportion = GetInt(content);
|
||||
}
|
||||
else if ( name == "best-size" )
|
||||
{
|
||||
pane.best_size = GetSize(content);
|
||||
}
|
||||
else if ( name == "min-size" )
|
||||
{
|
||||
pane.min_size = GetSize(content);
|
||||
}
|
||||
else if ( name == "max-size" )
|
||||
{
|
||||
pane.max_size = GetSize(content);
|
||||
}
|
||||
else if ( name == "floating-rect" )
|
||||
{
|
||||
auto rect = GetRect(content);
|
||||
|
||||
pane.floating_pos = rect.GetPosition();
|
||||
pane.floating_size = rect.GetSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unexpected pane child node name");
|
||||
}
|
||||
}
|
||||
|
||||
panes.push_back(pane);
|
||||
}
|
||||
}
|
||||
|
||||
return panes;
|
||||
}
|
||||
|
||||
virtual wxWindow* CreatePaneWindow(const wxAuiPaneInfo& pane) override
|
||||
{
|
||||
wxLogWarning("Unknown pane \"%s\"", pane.name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual std::vector<wxAuiDockInfo> LoadDocks() override
|
||||
{
|
||||
std::vector<wxAuiDockInfo> docks;
|
||||
|
||||
for ( wxXmlNode* node = m_docks->GetChildren(); node; node = node->GetNext() )
|
||||
{
|
||||
if ( node->GetName() != "dock" )
|
||||
throw std::runtime_error("Unexpected dock node name");
|
||||
|
||||
wxAuiDockInfo dock;
|
||||
if ( node->GetAttribute("resizable") == "1" )
|
||||
dock.resizable = true;
|
||||
|
||||
for ( wxXmlNode* child = node->GetChildren(); child; child = child->GetNext() )
|
||||
{
|
||||
const wxString& name = child->GetName();
|
||||
const wxString& content = child->GetNodeContent();
|
||||
|
||||
if ( name == "direction" )
|
||||
{
|
||||
dock.dock_direction = GetInt(content);
|
||||
}
|
||||
else if ( name == "layer" )
|
||||
{
|
||||
dock.dock_layer = GetInt(content);
|
||||
}
|
||||
else if ( name == "row" )
|
||||
{
|
||||
dock.dock_row = GetInt(content);
|
||||
}
|
||||
else if ( name == "size" )
|
||||
{
|
||||
dock.size = GetInt(content);
|
||||
}
|
||||
else if ( name == "min-size" )
|
||||
{
|
||||
dock.min_size = GetInt(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unexpected dock child node name");
|
||||
}
|
||||
}
|
||||
|
||||
docks.push_back(dock);
|
||||
}
|
||||
|
||||
return docks;
|
||||
}
|
||||
|
||||
private:
|
||||
int GetInt(const wxString& str)
|
||||
{
|
||||
int value;
|
||||
if ( !str.ToInt(&value) )
|
||||
throw std::runtime_error("Failed to parse integer");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
wxSize GetSize(const wxString& str)
|
||||
{
|
||||
wxString strH;
|
||||
const wxString strW = str.BeforeFirst('x', &strH);
|
||||
|
||||
unsigned int w, h;
|
||||
if ( !strW.ToUInt(&w) || !strH.ToUInt(&h) )
|
||||
throw std::runtime_error("Failed to parse size");
|
||||
|
||||
return wxSize(w, h);
|
||||
}
|
||||
|
||||
wxRect GetRect(const wxString& str)
|
||||
{
|
||||
wxString strWH;
|
||||
const wxString strXY = str.BeforeFirst(' ', &strWH);
|
||||
|
||||
wxString strY;
|
||||
const wxString strX = strXY.BeforeFirst(',', &strY);
|
||||
|
||||
unsigned int x, y;
|
||||
if ( !strX.ToUInt(&x) || !strY.ToUInt(&y) )
|
||||
throw std::runtime_error("Failed to parse position");
|
||||
|
||||
return wxRect(wxPoint(x, y), GetSize(strWH));
|
||||
}
|
||||
|
||||
|
||||
wxXmlDocument m_doc;
|
||||
wxXmlNode* m_panes = nullptr;
|
||||
wxXmlNode* m_docks = nullptr;
|
||||
};
|
||||
|
||||
void MyFrame::OnCopyLayout(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
MyXmlSerializer ser;
|
||||
m_mgr.SaveLayout(ser);
|
||||
|
||||
#if wxUSE_CLIPBOARD
|
||||
if (wxTheClipboard->Open())
|
||||
wxClipboardLocker clipLock;
|
||||
|
||||
wxTheClipboard->SetData(new wxTextDataObject(ser.GetXML()));
|
||||
#endif // wxUSE_CLIPBOARD
|
||||
}
|
||||
|
||||
void MyFrame::OnPasteLayout(wxCommandEvent& WXUNUSED(evt))
|
||||
{
|
||||
#if wxUSE_CLIPBOARD
|
||||
wxClipboardLocker clipLock;
|
||||
|
||||
wxTextDataObject data;
|
||||
if ( !wxTheClipboard->GetData(data) )
|
||||
{
|
||||
wxTheClipboard->SetData(new wxTextDataObject(s));
|
||||
wxTheClipboard->Close();
|
||||
wxLogError("Failed to get XML data from clipboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
MyXmlDeserializer deser(m_mgr, data.GetText());
|
||||
m_mgr.LoadLayout(deser);
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
wxLogError("Failed to load layout: %s", e.what());
|
||||
}
|
||||
#else
|
||||
wxLogError("This menu command requires wxUSE_CLIPBOARD.");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "wx/aui/floatpane.h"
|
||||
#include "wx/aui/tabmdi.h"
|
||||
#include "wx/aui/auibar.h"
|
||||
#include "wx/aui/serializer.h"
|
||||
#include "wx/mdi.h"
|
||||
#include "wx/wupdlock.h"
|
||||
|
||||
@@ -1340,6 +1341,205 @@ bool wxAuiManager::LoadPerspective(const wxString& layout, bool update)
|
||||
return true;
|
||||
}
|
||||
|
||||
// These helper functions are used by SaveLayout() and LoadLayout() below, as
|
||||
// we save the panes and docks geometries using DIPs on all platforms in order
|
||||
// to ensure that they're restored correctly if the display DPI changes between
|
||||
// saving and restoring the layout even on the platforms not using DIPs.
|
||||
namespace
|
||||
{
|
||||
|
||||
void MakeDIP(wxWindow* w, wxPoint& pos)
|
||||
{
|
||||
pos = w->ToDIP(pos);
|
||||
}
|
||||
|
||||
void MakeDIP(wxWindow* w, wxSize& size)
|
||||
{
|
||||
size = w->ToDIP(size);
|
||||
}
|
||||
|
||||
void MakeDIP(wxWindow* w, wxRect& rect)
|
||||
{
|
||||
rect = wxRect{w->ToDIP(rect.GetPosition()), w->ToDIP(rect.GetSize())};
|
||||
}
|
||||
|
||||
void MakeLogical(wxWindow* w, wxPoint& pos)
|
||||
{
|
||||
pos = w->FromDIP(pos);
|
||||
}
|
||||
|
||||
void MakeLogical(wxWindow* w, wxSize& size)
|
||||
{
|
||||
size = w->FromDIP(size);
|
||||
}
|
||||
|
||||
void MakeLogical(wxWindow* w, wxRect& rect)
|
||||
{
|
||||
rect = wxRect{w->FromDIP(rect.GetPosition()), w->FromDIP(rect.GetSize())};
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void wxAuiManager::SaveLayout(wxAuiSerializer& serializer) const
|
||||
{
|
||||
serializer.BeforeSave();
|
||||
|
||||
if ( !m_panes.empty() )
|
||||
{
|
||||
serializer.BeforeSavePanes();
|
||||
|
||||
for ( const auto& pane : m_panes )
|
||||
{
|
||||
auto paneDIP = pane;
|
||||
|
||||
MakeDIP(m_frame, paneDIP.best_size);
|
||||
MakeDIP(m_frame, paneDIP.min_size);
|
||||
MakeDIP(m_frame, paneDIP.max_size);
|
||||
|
||||
MakeDIP(m_frame, paneDIP.floating_pos);
|
||||
MakeDIP(m_frame, paneDIP.floating_size);
|
||||
|
||||
MakeDIP(m_frame, paneDIP.rect);
|
||||
|
||||
serializer.SavePane(paneDIP);
|
||||
}
|
||||
|
||||
serializer.AfterSavePanes();
|
||||
}
|
||||
|
||||
if ( !m_docks.empty() )
|
||||
{
|
||||
serializer.BeforeSaveDocks();
|
||||
|
||||
for ( const auto& dock : m_docks )
|
||||
{
|
||||
auto dockDIP = dock;
|
||||
|
||||
MakeDIP(m_frame, dockDIP.rect);
|
||||
|
||||
// Update dock sizes to ensure that restoring this layout later
|
||||
// restores the same geometry as is used now: if we didn't do it,
|
||||
// panes would have their initial sizes.
|
||||
switch ( dock.dock_direction )
|
||||
{
|
||||
case wxAUI_DOCK_TOP:
|
||||
case wxAUI_DOCK_BOTTOM:
|
||||
dockDIP.size = dock.rect.height;
|
||||
break;
|
||||
|
||||
case wxAUI_DOCK_LEFT:
|
||||
case wxAUI_DOCK_RIGHT:
|
||||
dockDIP.size = dock.rect.width;
|
||||
break;
|
||||
|
||||
case wxAUI_DOCK_CENTER:
|
||||
// Not clear what to do for this one, but it shouldn't
|
||||
// matter as its size is determined by what remains
|
||||
// available after positioning the rest of the elements, so
|
||||
// don't do anything.
|
||||
break;
|
||||
|
||||
case wxAUI_DOCK_NONE:
|
||||
wxFAIL_MSG("invalid dock direction");
|
||||
break;
|
||||
}
|
||||
|
||||
serializer.SaveDock(dockDIP);
|
||||
}
|
||||
|
||||
serializer.AfterSaveDocks();
|
||||
}
|
||||
|
||||
serializer.AfterSave();
|
||||
}
|
||||
|
||||
void wxAuiManager::LoadLayout(wxAuiDeserializer& deserializer)
|
||||
{
|
||||
deserializer.BeforeLoad();
|
||||
|
||||
// Note that we don't reset m_hasMaximized here but use a local variable so
|
||||
// as to avoid modifying m_hasMaximized in case one of the deserializer
|
||||
// functions called below throws an exception.
|
||||
bool hasMaximized = false;
|
||||
|
||||
// Also keep local variables for the existing (and possibly updated) panes
|
||||
// and the new ones for the same reason.
|
||||
wxAuiPaneInfoArray panes = m_panes;
|
||||
|
||||
struct NewPane
|
||||
{
|
||||
// In C++11 this ctor is required.
|
||||
NewPane(wxWindow* window_, const wxAuiPaneInfo& info_)
|
||||
: window(window_), info(info_)
|
||||
{
|
||||
}
|
||||
|
||||
wxWindow* window = nullptr;
|
||||
wxAuiPaneInfo info;
|
||||
};
|
||||
std::vector<NewPane> newPanes;
|
||||
|
||||
for ( const auto& paneDIP : deserializer.LoadPanes() )
|
||||
{
|
||||
auto pane = paneDIP;
|
||||
|
||||
MakeLogical(m_frame, pane.best_size);
|
||||
MakeLogical(m_frame, pane.min_size);
|
||||
MakeLogical(m_frame, pane.max_size);
|
||||
|
||||
MakeLogical(m_frame, pane.floating_pos);
|
||||
MakeLogical(m_frame, pane.floating_size);
|
||||
|
||||
MakeLogical(m_frame, pane.rect);
|
||||
|
||||
if ( pane.IsMaximized() )
|
||||
hasMaximized = true;
|
||||
|
||||
// Find the pane with the same name in the existing layout.
|
||||
bool found = false;
|
||||
for ( auto& existingPane : panes )
|
||||
{
|
||||
if ( existingPane.name == pane.name )
|
||||
{
|
||||
// Update the existing pane with the restored layout.
|
||||
existingPane.SafeSet(pane);
|
||||
|
||||
found = true;
|
||||
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 ( const auto w = deserializer.CreatePaneWindow(pane) )
|
||||
newPanes.emplace_back(w, pane);
|
||||
}
|
||||
}
|
||||
|
||||
wxAuiDockInfoArray docks;
|
||||
for ( const auto& dockDIP : deserializer.LoadDocks() )
|
||||
{
|
||||
auto dock = dockDIP;
|
||||
|
||||
MakeLogical(m_frame, dock.rect);
|
||||
|
||||
docks.push_back(dock);
|
||||
}
|
||||
|
||||
// After loading everything successfully, do update the internal variables.
|
||||
m_hasMaximized = hasMaximized;
|
||||
m_panes.swap(panes);
|
||||
|
||||
for ( const auto& newPane : newPanes )
|
||||
AddPane(newPane.window, newPane.info);
|
||||
|
||||
m_docks.swap(docks);
|
||||
|
||||
deserializer.AfterLoad();
|
||||
}
|
||||
|
||||
void wxAuiManager::GetPanePositionsAndSizes(wxAuiDockInfo& dock,
|
||||
wxArrayInt& positions,
|
||||
wxArrayInt& sizes)
|
||||
|
||||
Reference in New Issue
Block a user