mirror of https://github.com/aseprite/aseprite.git
Save/Load user defined layouts in new user.aseprite-layouts file
And now we store the TiXmlElement for each Layout, instead of converting from/to text back and forth.
This commit is contained in:
parent
1ac2a09818
commit
ec95c13a6b
|
@ -655,6 +655,7 @@ target_sources(app-lib PRIVATE
|
|||
ui/keyboard_shortcuts.cpp
|
||||
ui/layer_frame_comboboxes.cpp
|
||||
ui/layout.cpp
|
||||
ui/layouts.cpp
|
||||
ui/layout_selector.cpp
|
||||
ui/main_menu_bar.cpp
|
||||
ui/main_window.cpp
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
namespace app {
|
||||
|
||||
static void save_dock_layout(TiXmlElement& elem, const Dock* dock)
|
||||
static void save_dock_layout(TiXmlElement* elem, const Dock* dock)
|
||||
{
|
||||
for (const auto child : dock->children()) {
|
||||
const int side = dock->whichSideChildIsDocked(child);
|
||||
|
@ -52,7 +52,7 @@ static void save_dock_layout(TiXmlElement& elem, const Dock* dock)
|
|||
if (!sideStr.empty())
|
||||
childElem.SetAttribute("side", sideStr);
|
||||
|
||||
save_dock_layout(childElem, subdock);
|
||||
save_dock_layout(&childElem, subdock);
|
||||
}
|
||||
else {
|
||||
// Set the widget ID as the element name, e.g. <timeline />,
|
||||
|
@ -66,7 +66,7 @@ static void save_dock_layout(TiXmlElement& elem, const Dock* dock)
|
|||
childElem.SetAttribute("height", size.h);
|
||||
}
|
||||
|
||||
elem.InsertEndChild(childElem);
|
||||
elem->InsertEndChild(childElem);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,40 +136,36 @@ static void load_dock_layout(const TiXmlElement* elem, Dock* dock)
|
|||
}
|
||||
}
|
||||
|
||||
Layout::Layout(const std::string& name, const Dock* dock) : m_name(name)
|
||||
// static
|
||||
LayoutPtr Layout::MakeFromXmlElement(const TiXmlElement* layoutElem)
|
||||
{
|
||||
XmlDocumentRef doc(new TiXmlDocument());
|
||||
TiXmlElement layoutsElem("layouts");
|
||||
{
|
||||
TiXmlElement layoutElem("layout");
|
||||
layoutElem.SetAttribute("name", name);
|
||||
auto layout = std::make_shared<Layout>();
|
||||
if (auto name = layoutElem->Attribute("name"))
|
||||
layout->m_name = name;
|
||||
|
||||
save_dock_layout(layoutElem, dock);
|
||||
layout->m_elem.reset(layoutElem->Clone()->ToElement());
|
||||
return layout;
|
||||
}
|
||||
|
||||
layoutsElem.InsertEndChild(layoutElem);
|
||||
}
|
||||
// static
|
||||
LayoutPtr Layout::MakeFromDock(const std::string& name, const Dock* dock)
|
||||
{
|
||||
auto layout = std::make_shared<Layout>();
|
||||
layout->m_name = name;
|
||||
|
||||
TiXmlDeclaration declaration("1.0", "utf-8", "");
|
||||
doc->InsertEndChild(declaration);
|
||||
doc->InsertEndChild(layoutsElem);
|
||||
layout->m_elem = std::make_unique<TiXmlElement>("layout");
|
||||
layout->m_elem->SetAttribute("name", name);
|
||||
save_dock_layout(layout->m_elem.get(), dock);
|
||||
|
||||
std::stringstream s;
|
||||
s << *doc;
|
||||
m_data = s.str();
|
||||
return layout;
|
||||
}
|
||||
|
||||
bool Layout::loadLayout(Dock* dock) const
|
||||
{
|
||||
XmlDocumentRef doc(new TiXmlDocument);
|
||||
doc->Parse(m_data.c_str(), 0, TIXML_DEFAULT_ENCODING);
|
||||
TiXmlHandle handle(doc.get());
|
||||
|
||||
TiXmlElement* layoutElem = handle.FirstChild("layouts").FirstChild("layout").ToElement();
|
||||
|
||||
if (!layoutElem)
|
||||
if (!m_elem)
|
||||
return false;
|
||||
|
||||
TiXmlElement* elem = layoutElem->FirstChildElement();
|
||||
TiXmlElement* elem = m_elem->FirstChildElement();
|
||||
while (elem) {
|
||||
load_dock_layout(elem, dock);
|
||||
elem = elem->NextSiblingElement();
|
||||
|
|
|
@ -11,25 +11,29 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class TiXmlElement;
|
||||
|
||||
namespace app {
|
||||
class Dock;
|
||||
|
||||
class Layout {
|
||||
class Layout;
|
||||
using LayoutPtr = std::shared_ptr<Layout>;
|
||||
|
||||
class Layout final {
|
||||
public:
|
||||
Layout(const std::string& name, const Dock* dock);
|
||||
static LayoutPtr MakeFromXmlElement(const TiXmlElement* layoutElem);
|
||||
static LayoutPtr MakeFromDock(const std::string& name, const Dock* dock);
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
const std::string& data() const { return m_data; }
|
||||
const TiXmlElement* xmlElement() const { return m_elem.get(); }
|
||||
|
||||
bool loadLayout(Dock* dock) const;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_data;
|
||||
std::unique_ptr<TiXmlElement> m_elem;
|
||||
};
|
||||
|
||||
using LayoutPtr = std::shared_ptr<Layout>;
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
||||
|
|
|
@ -128,13 +128,13 @@ public:
|
|||
case LayoutId::SAVE_LAYOUT: {
|
||||
gen::NewLayout window;
|
||||
window.name()->setText(
|
||||
fmt::format("{} ({})", window.name()->text(), m_selector->m_layouts.size()));
|
||||
fmt::format("{} ({})", window.name()->text(), m_selector->m_layouts.size() + 1));
|
||||
|
||||
window.openWindowInForeground();
|
||||
if (window.closer() == window.ok()) {
|
||||
auto layout = std::make_shared<Layout>(window.name()->text(), win->customizableDock());
|
||||
auto layout = Layout::MakeFromDock(window.name()->text(), win->customizableDock());
|
||||
|
||||
m_selector->addLayout(std::move(layout));
|
||||
m_selector->addLayout(layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ void LayoutSelector::addLayout(const LayoutPtr& layout)
|
|||
auto item = m_comboBox.addItem(
|
||||
new LayoutItem(this, LayoutItem::USER_DEFINED, layout->name(), layout));
|
||||
|
||||
m_layouts.push_back(layout);
|
||||
m_layouts.addLayout(layout);
|
||||
|
||||
m_comboBox.setSelectedItemIndex(item);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "app/ui/dockable.h"
|
||||
#include "app/ui/icon_button.h"
|
||||
#include "app/ui/layout.h"
|
||||
#include "app/ui/layouts.h"
|
||||
#include "ui/animated_widget.h"
|
||||
#include "ui/box.h"
|
||||
#include "ui/combobox.h"
|
||||
|
@ -61,7 +62,7 @@ private:
|
|||
IconButton m_button;
|
||||
gfx::Size m_startSize;
|
||||
gfx::Size m_endSize;
|
||||
std::vector<LayoutPtr> m_layouts;
|
||||
Layouts m_layouts;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Aseprite
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/layouts.h"
|
||||
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/xml_document.h"
|
||||
#include "app/xml_exception.h"
|
||||
#include "base/fs.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace app {
|
||||
|
||||
Layouts::Layouts()
|
||||
{
|
||||
try {
|
||||
std::string fn = m_userLayoutsFilename = UserLayoutsFilename();
|
||||
if (base::is_file(fn))
|
||||
load(fn);
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
LOG(ERROR, "LAY: Error loading user layouts: %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
Layouts::~Layouts()
|
||||
{
|
||||
if (!m_userLayoutsFilename.empty())
|
||||
save(m_userLayoutsFilename);
|
||||
}
|
||||
|
||||
void Layouts::addLayout(const LayoutPtr& layout)
|
||||
{
|
||||
m_layouts.push_back(layout);
|
||||
}
|
||||
|
||||
void Layouts::load(const std::string& fn)
|
||||
{
|
||||
XmlDocumentRef doc = app::open_xml(fn);
|
||||
TiXmlHandle handle(doc.get());
|
||||
TiXmlElement* layoutElem = handle.FirstChild("layouts").FirstChild("layout").ToElement();
|
||||
|
||||
while (layoutElem) {
|
||||
m_layouts.push_back(Layout::MakeFromXmlElement(layoutElem));
|
||||
layoutElem = layoutElem->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
|
||||
void Layouts::save(const std::string& fn) const
|
||||
{
|
||||
XmlDocumentRef doc(new TiXmlDocument());
|
||||
TiXmlElement layoutsElem("layouts");
|
||||
|
||||
for (const auto& layout : m_layouts)
|
||||
layoutsElem.InsertEndChild(*layout->xmlElement());
|
||||
|
||||
TiXmlDeclaration declaration("1.0", "utf-8", "");
|
||||
doc->InsertEndChild(declaration);
|
||||
doc->InsertEndChild(layoutsElem);
|
||||
save_xml(doc, fn);
|
||||
}
|
||||
|
||||
// static
|
||||
std::string Layouts::UserLayoutsFilename()
|
||||
{
|
||||
ResourceFinder rf;
|
||||
rf.includeUserDir("user.aseprite-layouts");
|
||||
return rf.getFirstOrCreateDefault();
|
||||
}
|
||||
|
||||
} // namespace app
|
|
@ -0,0 +1,44 @@
|
|||
// Aseprite
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_LAYOUTS_H_INCLUDED
|
||||
#define APP_UI_LAYOUTS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/layout.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace app {
|
||||
|
||||
class Layouts {
|
||||
public:
|
||||
Layouts();
|
||||
~Layouts();
|
||||
|
||||
size_t size() const { return m_layouts.size(); }
|
||||
|
||||
void addLayout(const LayoutPtr& layout);
|
||||
|
||||
// To iterate layouts
|
||||
using List = std::vector<LayoutPtr>;
|
||||
using iterator = List::iterator;
|
||||
iterator begin() { return m_layouts.begin(); }
|
||||
iterator end() { return m_layouts.end(); }
|
||||
|
||||
private:
|
||||
void load(const std::string& fn);
|
||||
void save(const std::string& fn) const;
|
||||
static std::string UserLayoutsFilename();
|
||||
|
||||
List m_layouts;
|
||||
std::string m_userLayoutsFilename;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue