24#include <wx/aui/framemanager.h>
25#if wxCHECK_VERSION( 3, 3, 0 )
26#include <wx/aui/serializer.h>
27#include <wx/aui/auibook.h>
32#include <nlohmann/json.hpp>
43#if wxCHECK_VERSION( 3, 3, 0 )
47 wxAuiPaneInfo* paneInfo;
58static wxString getWindowName( wxWindow* aWindow )
63 wxString
name = aWindow->GetName();
67 name = aWindow->GetLabel();
72static PANE_METADATA buildMetadata( wxAuiPaneInfo& aInfo,
size_t aIndex )
76 meta.paneInfo = &aInfo;
77 meta.name = aInfo.name;
78 meta.caption = aInfo.caption;
79 meta.isToolbar = aInfo.IsToolbar();
80 meta.isCenter = ( aInfo.dock_direction == wxAUI_DOCK_CENTER );
81 meta.isNotebook = aInfo.window && wxDynamicCast( aInfo.window, wxAuiNotebook );
86 meta.windowName = getWindowName( aInfo.window );
88 if( aInfo.window->GetClassInfo() )
89 meta.className = aInfo.window->GetClassInfo()->GetClassName();
95static void addDockLayout( nlohmann::json& aNode,
const wxAuiDockLayoutInfo& aLayout )
97 nlohmann::json dock = nlohmann::json::object();
99 dock[
"direction"] = aLayout.dock_direction;
100 dock[
"layer"] = aLayout.dock_layer;
101 dock[
"row"] = aLayout.dock_row;
102 dock[
"position"] = aLayout.dock_pos;
103 dock[
"proportion"] = aLayout.dock_proportion;
104 dock[
"size"] = aLayout.dock_size;
106 aNode[
"dock"] = std::move( dock );
109static void readDockLayout(
const nlohmann::json& aNode, wxAuiDockLayoutInfo& aLayout )
111 if( !aNode.is_object() )
114 aLayout.dock_direction = aNode.value(
"direction", aLayout.dock_direction );
115 aLayout.dock_layer = aNode.value(
"layer", aLayout.dock_layer );
116 aLayout.dock_row = aNode.value(
"row", aLayout.dock_row );
117 aLayout.dock_pos = aNode.value(
"position", aLayout.dock_pos );
118 aLayout.dock_proportion = aNode.value(
"proportion", aLayout.dock_proportion );
119 aLayout.dock_size = aNode.value(
"size", aLayout.dock_size );
122class JSON_SERIALIZER :
public wxAuiSerializer
125 explicit JSON_SERIALIZER( wxAuiManager& aManager ) : m_manager( aManager ), m_paneIndex( 0 )
127 m_root = nlohmann::json::object();
130 nlohmann::json GetState()
const
135 void BeforeSave()
override
137 m_root[
"format"] =
"kicad.wxaui";
138 m_root[
"version"] = 1;
141 void BeforeSavePanes()
override
143 m_panes = nlohmann::json::array();
147 void SavePane(
const wxAuiPaneLayoutInfo& aPane )
override
149 nlohmann::json pane = nlohmann::json::object();
151 pane[
"name"] = aPane.name.ToStdString();
153 addDockLayout( pane, aPane );
155 if( aPane.floating_pos != wxDefaultPosition || aPane.floating_size != wxDefaultSize )
157 nlohmann::json floating = nlohmann::json::object();
158 floating[
"rect"] = wxRect( aPane.floating_pos, aPane.floating_size );
160 pane[
"floating"] = std::move( floating );
163 if( aPane.is_maximized )
164 pane[
"maximized"] =
true;
166 if( aPane.is_hidden )
167 pane[
"hidden"] =
true;
169 wxAuiPaneInfo&
info = m_manager.GetPane( aPane.name );
171 nlohmann::json meta = nlohmann::json::object();
172 meta[
"toolbar"] =
info.IsToolbar();
173 meta[
"center"] = (
info.dock_direction == wxAUI_DOCK_CENTER );
174 meta[
"notebook"] =
info.window && wxDynamicCast(
info.window, wxAuiNotebook );
175 meta[
"index"] = m_paneIndex++;
179 wxString windowName = getWindowName(
info.window );
181 if( !windowName.IsEmpty() )
182 meta[
"window_name"] = windowName.ToStdString();
184 if(
info.window->GetClassInfo() )
185 meta[
"class_name"] = wxString(
info.window->GetClassInfo()->GetClassName() ).ToStdString();
188 if( !
info.caption.IsEmpty() )
189 meta[
"caption"] =
info.caption.ToStdString();
191 pane[
"meta"] = std::move( meta );
193 m_panes.push_back( std::move( pane ) );
196 void AfterSavePanes()
override
198 m_root[
"panes"] = std::move( m_panes );
201 void BeforeSaveNotebooks()
override
203 m_notebooks = nlohmann::json::array();
206 void BeforeSaveNotebook(
const wxString& aName )
override
208 m_currentNotebook = nlohmann::json::object();
209 m_currentNotebook[
"name"] = aName.ToStdString();
211 wxAuiPaneInfo&
info = m_manager.GetPane( aName );
212 nlohmann::json meta = nlohmann::json::object();
216 wxString windowName = getWindowName(
info.window );
218 if( !windowName.IsEmpty() )
219 meta[
"window_name"] = windowName.ToStdString();
221 if(
info.window->GetClassInfo() )
222 meta[
"class_name"] = wxString(
info.window->GetClassInfo()->GetClassName() ).ToStdString();
225 if( !
info.caption.IsEmpty() )
226 meta[
"caption"] =
info.caption.ToStdString();
228 meta[
"toolbar"] =
info.IsToolbar();
229 meta[
"center"] = (
info.dock_direction == wxAUI_DOCK_CENTER );
230 meta[
"notebook"] =
true;
232 m_currentNotebook[
"meta"] = std::move( meta );
233 m_currentNotebook[
"tabs"] = nlohmann::json::array();
236 void SaveNotebookTabControl(
const wxAuiTabLayoutInfo& aTab )
override
238 nlohmann::json tab = nlohmann::json::object();
240 addDockLayout( tab, aTab );
242 if( !aTab.pages.empty() )
243 tab[
"pages"] = aTab.pages;
245 if( !aTab.pinned.empty() )
246 tab[
"pinned"] = aTab.pinned;
248 if( aTab.active >= 0 )
249 tab[
"active"] = aTab.active;
251 m_currentNotebook[
"tabs"].push_back( std::move( tab ) );
254 void AfterSaveNotebook()
override
256 m_notebooks.push_back( std::move( m_currentNotebook ) );
257 m_currentNotebook = nlohmann::json();
260 void AfterSaveNotebooks()
override
262 if( !m_notebooks.empty() )
263 m_root[
"notebooks"] = std::move( m_notebooks );
266 void AfterSave()
override {}
269 wxAuiManager& m_manager;
270 nlohmann::json m_root;
271 nlohmann::json m_panes;
272 nlohmann::json m_notebooks;
273 nlohmann::json m_currentNotebook;
277class JSON_DESERIALIZER :
public wxAuiDeserializer
280 JSON_DESERIALIZER( wxAuiManager& aManager,
const nlohmann::json& aState )
281 : wxAuiDeserializer( aManager ), m_manager( aManager ), m_state( aState )
283 if( !m_state.is_object() )
284 throw std::runtime_error(
"Invalid AUI layout state" );
286 const std::string format = m_state.value(
"format", std::string() );
288 if( format !=
"kicad.wxaui" )
289 throw std::runtime_error(
"Unsupported AUI layout format" );
291 int version = m_state.value(
"version", 0 );
294 throw std::runtime_error(
"Unsupported AUI layout version" );
296 if( m_state.contains(
"panes" ) && m_state[
"panes"].is_array() )
297 m_serializedPanes = m_state[
"panes"].get<std::vector<nlohmann::json>>();
299 if( m_state.contains(
"notebooks" ) && m_state[
"notebooks"].is_array() )
300 m_serializedNotebooks = m_state[
"notebooks"].get<std::vector<nlohmann::json>>();
303 std::vector<wxAuiPaneLayoutInfo> LoadPanes()
override
305 std::vector<wxAuiPaneLayoutInfo> panes;
307 wxAuiPaneInfoArray paneArray = m_manager.GetAllPanes();
309 std::vector<PANE_METADATA> metadata;
310 metadata.reserve( paneArray.GetCount() );
312 for(
size_t i = 0; i < paneArray.GetCount(); ++i )
313 metadata.push_back( buildMetadata( paneArray[i], i ) );
315 std::set<wxAuiPaneInfo*> used;
317 for(
const nlohmann::json& jsonPane : m_serializedPanes )
319 if( !jsonPane.is_object() )
322 wxAuiPaneLayoutInfo pane( wxString::FromUTF8( jsonPane.value(
"name", std::string() ) ) );
324 readDockLayout( jsonPane.value(
"dock", nlohmann::json::object() ), pane );
326 if( jsonPane.contains(
"floating" ) )
328 const nlohmann::json& floating = jsonPane[
"floating"];
330 if( floating.contains(
"rect" ) )
332 wxRect rect = floating[
"rect"].get<wxRect>();
333 pane.floating_pos = rect.GetPosition();
334 pane.floating_size = rect.GetSize();
338 pane.is_maximized = jsonPane.value(
"maximized",
false );
339 pane.is_hidden = jsonPane.value(
"hidden",
false );
341 wxAuiPaneInfo* actualPane = matchPane( jsonPane, metadata, used );
345 pane.name = actualPane->name;
346 used.insert( actualPane );
347 panes.push_back( pane );
354 std::vector<wxAuiTabLayoutInfo> LoadNotebookTabs(
const wxString& aName )
override
356 const wxAuiPaneInfo& paneInfo = m_manager.GetPane( aName );
358 auto loadTabs = [](
const nlohmann::json& aNotebook )
360 std::vector<wxAuiTabLayoutInfo> tabs;
362 if( !aNotebook.contains(
"tabs" ) || !aNotebook[
"tabs"].is_array() )
365 for(
const nlohmann::json& tabJson : aNotebook[
"tabs"].get<std::vector<nlohmann::json>>() )
367 if( !tabJson.is_object() )
370 wxAuiTabLayoutInfo
info;
372 readDockLayout( tabJson.value(
"dock", nlohmann::json::object() ),
info );
374 if( tabJson.contains(
"pages" ) )
377 for(
int page : tabJson[
"pages"].get<std::vector<int>>() )
378 info.pages.push_back( page );
381 if( tabJson.contains(
"pinned" ) )
384 for(
int page : tabJson[
"pinned"].get<std::vector<int>>() )
385 info.pinned.push_back( page );
388 info.active = tabJson.value(
"active",
info.active );
390 tabs.push_back(
info );
396 for(
const nlohmann::json& notebook : m_serializedNotebooks )
398 if( !notebook.is_object() )
401 wxString storedName = wxString::FromUTF8( notebook.value(
"name", std::string() ) );
403 if( storedName == aName )
404 return loadTabs( notebook );
407 if( !paneInfo.IsOk() )
410 wxString windowName = paneInfo.window ? getWindowName( paneInfo.window ) : wxString();
413 if( paneInfo.window && paneInfo.window->GetClassInfo() )
414 className = paneInfo.window->GetClassInfo()->GetClassName();
416 for(
const nlohmann::json& notebook : m_serializedNotebooks )
418 if( !notebook.is_object() )
421 const nlohmann::json& meta = notebook.value(
"meta", nlohmann::json::object() );
423 const wxString storedWindowName = wxString::FromUTF8( meta.value(
"window_name", std::string() ) );
424 const wxString storedClassName = wxString::FromUTF8( meta.value(
"class_name", std::string() ) );
426 const bool toolbar = meta.value(
"toolbar", false );
427 const bool center = meta.value(
"center", false );
428 const bool notebookFlag = meta.value(
"notebook", false );
430 if( toolbar != paneInfo.IsToolbar() || center != ( paneInfo.dock_direction == wxAUI_DOCK_CENTER ) )
436 if( !windowName.IsEmpty() && !storedWindowName.IsEmpty() && windowName == storedWindowName )
437 return loadTabs( notebook );
439 if( !className.IsEmpty() && !storedClassName.IsEmpty() && className == storedClassName )
440 return loadTabs( notebook );
446 wxWindow* CreatePaneWindow( wxAuiPaneInfo& )
override
452 wxAuiPaneInfo* matchPane(
const nlohmann::json& aJson,
453 const std::vector<PANE_METADATA>& aMetadata,
454 const std::set<wxAuiPaneInfo*>& aUsed )
const
456 auto selectCandidate = [&aUsed]( wxAuiPaneInfo* pane ) ->
bool
458 return pane && aUsed.find( pane ) == aUsed.end();
461 wxString storedName = wxString::FromUTF8( aJson.value(
"name", std::string() ) );
463 if( !storedName.IsEmpty() )
465 wxAuiPaneInfo& pane = m_manager.GetPane( storedName );
467 if( pane.IsOk() && selectCandidate( &pane ) )
471 wxAuiPaneInfo* match =
nullptr;
472 const nlohmann::json& meta = aJson.value(
"meta", nlohmann::json::object() );
474 const wxString windowName = wxString::FromUTF8( meta.value(
"window_name", std::string() ) );
475 const wxString className = wxString::FromUTF8( meta.value(
"class_name", std::string() ) );
476 const wxString caption = wxString::FromUTF8( meta.value(
"caption", std::string() ) );
477 const bool isToolbar = meta.value(
"toolbar",
false );
478 const bool isCenter = meta.value(
"center",
false );
479 const bool isNotebook = meta.value(
"notebook",
false );
480 const size_t index = meta.value(
"index", std::numeric_limits<size_t>::max() );
482 auto findBy = [&](
auto predicate ) -> wxAuiPaneInfo*
484 wxAuiPaneInfo* candidate =
nullptr;
486 for(
const PANE_METADATA& data : aMetadata )
488 if( !predicate( data ) )
491 if( !selectCandidate( data.paneInfo ) )
497 candidate = data.paneInfo;
503 if( !windowName.IsEmpty() )
505 match = findBy( [&](
const PANE_METADATA& data )
507 return data.windowName == windowName;
514 if( !className.IsEmpty() )
516 match = findBy( [&](
const PANE_METADATA& data )
518 if( data.className != className )
521 if( data.isToolbar != isToolbar )
524 if( data.isCenter != isCenter )
527 if( data.isNotebook != isNotebook )
537 if( index != std::numeric_limits<size_t>::max() )
539 match = findBy( [&](
const PANE_METADATA& data )
541 return data.index == index;
548 if( !caption.IsEmpty() )
550 match = findBy( [&](
const PANE_METADATA& data )
552 return data.caption == caption;
559 match = findBy( [&](
const PANE_METADATA& data )
561 if( data.isToolbar != isToolbar )
564 if( data.isNotebook != isNotebook )
573 wxAuiManager& m_manager;
574 nlohmann::json m_state;
575 std::vector<nlohmann::json> m_serializedPanes;
576 std::vector<nlohmann::json> m_serializedNotebooks;
588#if wxCHECK_VERSION( 3, 3, 0 )
593 return serializer.GetState();
595 catch(
const std::exception& err )
597 wxLogWarning(
"Failed to serialize AUI layout: %s", err.what() );
601 return nlohmann::json();
606#if wxCHECK_VERSION( 3, 3, 0 )
607 if( aState.is_null() || aState.empty() )
612 JSON_DESERIALIZER deserializer(
m_manager, aState );
616 catch(
const std::exception& err )
618 wxLogWarning(
"Failed to deserialize AUI layout: %s", err.what() );
bool Deserialize(const nlohmann::json &aState) const
nlohmann::json Serialize() const
WX_AUI_JSON_SERIALIZER(wxAuiManager &aManager)