metaforce/Editor/Space.hpp

379 lines
12 KiB
C++
Raw Normal View History

2018-10-06 20:42:33 -07:00
#pragma once
2017-12-29 00:08:12 -08:00
#include "athena/DNAYaml.hpp"
#include "specter/Space.hpp"
#include "specter/IMenuNode.hpp"
#include "specter/Button.hpp"
#include "specter/MultiLineTextView.hpp"
#include "specter/ViewResources.hpp"
#include "specter/RootView.hpp"
2016-01-04 16:01:02 -08:00
#include "ProjectManager.hpp"
2018-12-07 21:30:43 -08:00
namespace specter {
class View;
class SplitView;
class ViewResources;
class Toolbar;
2017-12-29 00:08:12 -08:00
struct Icon;
2018-12-07 21:30:43 -08:00
} // namespace specter
2021-04-10 01:42:06 -07:00
namespace metaforce {
class ViewManager;
class RootSpace;
2018-10-06 19:59:17 -07:00
class SplitSpace;
2018-12-07 21:30:43 -08:00
class Space : public specter::ISpaceController {
friend class SplitSpace;
public:
2018-12-07 21:30:43 -08:00
virtual ~Space() = default;
Space(const Space& other) = delete;
Space& operator=(const Space& other) = delete;
/** Common encoded-enumeration for all space classes */
enum class Class {
None,
RootSpace,
SplitSpace,
TestSpace,
ResourceBrowser,
ModelViewer,
EffectEditor,
InformationCenter,
GameMode
};
struct State : athena::io::DNAVYaml<athena::Endian::Big> {
2018-12-07 21:30:43 -08:00
Delete _d;
};
static Space* NewSpaceFromConfigStream(ViewManager& vm, Space* parent, ConfigReader& r);
static RootSpace* NewRootSpaceFromConfigStream(ViewManager& vm, ConfigReader& r);
struct SpaceMenuNode final : specter::IMenuNode {
struct SubNodeData final : specter::IMenuNode {
Class m_cls;
std::string m_text;
specter::Icon& m_icon;
zeus::CColor m_color;
const std::string* text() const override { return &m_text; }
void activated(const boo::SWindowCoord& coord) override {}
2018-12-07 21:30:43 -08:00
2019-07-19 21:27:21 -07:00
SubNodeData(Class cls, const char* text, specter::Icon& icon, const zeus::CColor& color)
: m_cls(cls), m_text(text), m_icon(icon), m_color(color) {}
};
2018-12-07 21:30:43 -08:00
static std::vector<SubNodeData> s_subNodeDats;
2016-01-03 21:31:02 -08:00
2018-12-07 21:30:43 -08:00
struct SubNode final : specter::IMenuNode {
Space& m_space;
const SubNodeData& m_data;
const std::string* text() const override { return &m_data.m_text; }
void activated(const boo::SWindowCoord& coord) override;
2016-01-16 20:14:13 -08:00
2018-12-07 21:30:43 -08:00
SubNode(Space& space, const SubNodeData& data) : m_space(space), m_data(data) {}
};
std::vector<SubNode> m_subNodes;
2018-12-07 21:30:43 -08:00
SpaceMenuNode(Space& space) {
m_subNodes.reserve(s_subNodeDats.size());
for (const SubNodeData& sn : s_subNodeDats)
m_subNodes.emplace_back(space, sn);
}
2018-12-07 21:30:43 -08:00
static std::string s_text;
const std::string* text() const override { return &s_text; }
2016-01-31 17:13:45 -08:00
size_t subNodeCount() const override { return m_subNodes.size(); }
IMenuNode* subNode(size_t idx) override { return &m_subNodes[idx]; }
2018-12-07 21:30:43 -08:00
static void InitializeStrings(ViewManager& vm);
static const std::string* LookupClassString(Class cls) {
for (const SubNodeData& sn : s_subNodeDats)
if (sn.m_cls == cls)
return &sn.m_text;
return nullptr;
}
2018-12-07 21:30:43 -08:00
static specter::Icon* LookupClassIcon(Class cls) {
for (SubNodeData& sn : s_subNodeDats)
if (sn.m_cls == cls)
return &sn.m_icon;
return nullptr;
}
static const zeus::CColor* LookupClassColor(Class cls) {
for (SubNodeData& sn : s_subNodeDats)
if (sn.m_cls == cls)
return &sn.m_color;
return nullptr;
}
2018-12-07 21:30:43 -08:00
} m_spaceMenuNode;
2018-12-07 21:30:43 -08:00
struct SpaceSelectBind : specter::IButtonBinding {
Space& m_space;
std::string_view name(const specter::Control* control) const override { return SpaceMenuNode::s_text; }
MenuStyle menuStyle(const specter::Button* button) const override { return MenuStyle::Primary; }
std::unique_ptr<specter::View> buildMenu(const specter::Button* button) override;
2018-12-07 21:30:43 -08:00
SpaceSelectBind(Space& space) : m_space(space) {}
} m_spaceSelectBind;
std::unique_ptr<specter::Button> m_spaceSelectButton;
2018-12-07 21:30:43 -08:00
protected:
friend class ViewManager;
friend class RootSpace;
ViewManager& m_vm;
Class m_class = Class::None;
Space* m_parent;
std::unique_ptr<specter::Space> m_spaceView;
Space(ViewManager& vm, Class cls, Space* parent);
/* Allows common Space code to access DNA-encoded state */
virtual const Space::State& spaceState() const = 0;
/* Structural control */
virtual bool usesToolbar() const { return false; }
virtual unsigned toolbarUnits() const { return 1; }
virtual void buildToolbarView(specter::ViewResources& res, specter::Toolbar& tb) {}
virtual specter::View* buildContentView(specter::ViewResources& res) = 0;
virtual specter::View* buildSpaceView(specter::ViewResources& res);
2018-12-07 21:30:43 -08:00
public:
virtual void saveState(athena::io::IStreamWriter& w) const;
virtual void saveState(athena::io::YAMLDocWriter& w) const;
virtual void reloadState() {}
virtual void think() {}
2018-12-07 21:30:43 -08:00
virtual Space* copy(Space* parent) const = 0;
bool spaceSplitAllowed() const override { return true; }
specter::ISplitSpaceController* spaceSplit(specter::SplitView::Axis axis, int thisSlot) override;
2018-12-07 21:30:43 -08:00
virtual std::unique_ptr<Space> exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) {
return std::unique_ptr<Space>();
}
2016-01-31 17:13:45 -08:00
2018-12-07 21:30:43 -08:00
virtual specter::View* basisView() { return m_spaceView.get(); }
Class cls() const { return m_class; }
SplitSpace* castToSplitSpace();
};
2018-12-07 21:30:43 -08:00
class RootSpace : public Space {
friend class ViewManager;
std::unique_ptr<specter::RootView> m_rootView;
std::unique_ptr<Space> m_spaceTree;
2019-08-10 17:49:41 -07:00
struct State : Space::State{AT_DECL_DNA_YAMLV } m_state;
const Space::State& spaceState() const override { return m_state; }
public:
2018-12-07 21:30:43 -08:00
RootSpace(ViewManager& vm) : Space(vm, Class::RootSpace, nullptr) {}
RootSpace(ViewManager& vm, ConfigReader& r) : RootSpace(vm) {
m_state.read(r);
if (auto rec = r.enterSubRecord("spaceTree"))
m_spaceTree.reset(NewSpaceFromConfigStream(vm, this, r));
}
void think() override {
2021-04-03 09:48:39 -07:00
OPTICK_EVENT();
2018-12-07 21:30:43 -08:00
if (m_spaceTree)
m_spaceTree->think();
}
void saveState(athena::io::IStreamWriter& w) const override {
2018-12-07 21:30:43 -08:00
w.writeUint32Big(atUint32(m_class));
m_state.write(w);
if (m_spaceTree)
m_spaceTree->saveState(w);
else
w.writeUint32Big(0);
}
void saveState(athena::io::YAMLDocWriter& w) const override {
2018-12-07 21:30:43 -08:00
w.writeUint32("class", atUint32(m_class));
m_state.write(w);
if (auto rec = w.enterSubRecord("spaceTree")) {
if (m_spaceTree)
m_spaceTree->saveState(w);
else
w.writeUint32("class", 0);
2016-01-10 18:17:08 -08:00
}
2018-12-07 21:30:43 -08:00
}
2016-01-10 18:17:08 -08:00
2018-12-07 21:30:43 -08:00
void setChild(std::unique_ptr<Space>&& space) {
m_spaceTree = std::move(space);
m_spaceTree->m_parent = this;
}
2016-01-03 21:31:02 -08:00
Space* copy(Space* parent) const override { return nullptr; }
bool spaceSplitAllowed() const override { return false; }
2018-12-07 21:30:43 -08:00
specter::View* buildSpaceView(specter::ViewResources& res) override;
specter::View* buildContentView(specter::ViewResources& res) override { return m_spaceTree->buildSpaceView(res); }
std::unique_ptr<Space> exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) override;
2016-01-04 16:01:02 -08:00
specter::View* basisView() override;
2018-12-07 21:30:43 -08:00
};
2016-01-04 16:01:02 -08:00
2018-12-07 21:30:43 -08:00
class SplitSpace : public Space, public specter::ISplitSpaceController {
friend class ViewManager;
std::unique_ptr<Space> m_slots[2];
std::unique_ptr<specter::SplitView> m_splitView;
struct State : Space::State {
2019-08-10 17:49:41 -07:00
AT_DECL_DNA_YAMLV
2018-12-07 21:30:43 -08:00
Value<specter::SplitView::Axis> axis = specter::SplitView::Axis::Horizontal;
Value<float> split = 0.5;
} m_state;
const Space::State& spaceState() const override { return m_state; }
2016-01-04 16:01:02 -08:00
2018-12-07 21:30:43 -08:00
public:
SplitSpace(ViewManager& vm, Space* parent, specter::SplitView::Axis axis) : Space(vm, Class::SplitSpace, parent) {
m_state.axis = axis;
reloadState();
}
SplitSpace(ViewManager& vm, Space* parent, ConfigReader& r)
: SplitSpace(vm, parent, specter::SplitView::Axis::Horizontal) {
m_state.read(r);
if (auto rec = r.enterSubRecord("slot0"))
m_slots[0].reset(NewSpaceFromConfigStream(vm, this, r));
if (auto rec = r.enterSubRecord("slot1"))
m_slots[1].reset(NewSpaceFromConfigStream(vm, this, r));
reloadState();
}
void reloadState() override {
2018-12-07 21:30:43 -08:00
m_state.split = std::min(1.f, std::max(0.f, m_state.split));
if (m_state.axis != specter::SplitView::Axis::Horizontal && m_state.axis != specter::SplitView::Axis::Vertical)
m_state.axis = specter::SplitView::Axis::Horizontal;
if (m_splitView) {
m_splitView->setSplit(m_state.split);
m_splitView->setAxis(m_state.axis);
}
}
void think() override {
2018-12-07 21:30:43 -08:00
if (m_slots[0])
m_slots[0]->think();
if (m_slots[1])
m_slots[1]->think();
}
void saveState(athena::io::IStreamWriter& w) const override {
2018-12-07 21:30:43 -08:00
w.writeUint32Big(atUint32(m_class));
m_state.write(w);
if (m_slots[0])
m_slots[0]->saveState(w);
else
w.writeUint32Big(0);
if (m_slots[1])
m_slots[1]->saveState(w);
else
w.writeUint32Big(0);
}
void saveState(athena::io::YAMLDocWriter& w) const override {
2018-12-07 21:30:43 -08:00
w.writeUint32("class", atUint32(m_class));
m_state.write(w);
if (auto rec = w.enterSubRecord("slot0")) {
if (m_slots[0])
m_slots[0]->saveState(w);
else
w.writeUint32("class", 0);
2016-01-04 16:01:02 -08:00
}
2018-12-07 21:30:43 -08:00
if (auto rec = w.enterSubRecord("slot1")) {
if (m_slots[1])
m_slots[1]->saveState(w);
else
w.writeUint32("class", 0);
2016-01-04 16:01:02 -08:00
}
2018-12-07 21:30:43 -08:00
}
2016-01-04 16:01:02 -08:00
2018-12-07 21:30:43 -08:00
void setChildSlot(unsigned slot, std::unique_ptr<Space>&& space);
2016-01-04 16:01:02 -08:00
specter::View* buildSpaceView(specter::ViewResources& res) override { return buildContentView(res); }
specter::View* buildContentView(specter::ViewResources& res) override;
Space* copy(Space* parent) const override { return nullptr; }
bool spaceSplitAllowed() const override { return false; }
2018-12-07 21:30:43 -08:00
ISpaceController* spaceJoin(int keepSlot) {
if (m_parent) {
ISpaceController* ret = m_slots[keepSlot].get();
m_parent->exchangeSpaceSplitJoin(this, std::move(m_slots[keepSlot]));
return ret;
}
2018-12-07 21:30:43 -08:00
return nullptr;
}
std::unique_ptr<Space> exchangeSpaceSplitJoin(Space* removeSpace, std::unique_ptr<Space>&& keepSpace) override;
specter::SplitView* splitView() override { return m_splitView.get(); }
void updateSplit(float split) override { m_state.split = split; }
void joinViews(specter::SplitView* thisSplit, int thisSlot, specter::SplitView* otherSplit, int otherSlot) override;
2016-01-31 17:13:45 -08:00
2018-12-07 21:30:43 -08:00
void setAxis(specter::SplitView::Axis axis) {
m_state.axis = axis;
reloadState();
}
2016-01-11 16:46:27 -08:00
2018-12-07 21:30:43 -08:00
specter::SplitView::Axis axis() const { return m_state.axis; }
float split() const { return m_state.split; }
2016-01-31 17:13:45 -08:00
specter::View* basisView() override { return m_splitView.get(); }
};
2018-12-07 21:30:43 -08:00
inline SplitSpace* Space::castToSplitSpace() {
return cls() == Class::SplitSpace ? static_cast<SplitSpace*>(this) : nullptr;
}
2018-12-07 21:30:43 -08:00
class ViewerSpace : public Space {
2016-02-01 12:04:55 -08:00
public:
2018-12-07 21:30:43 -08:00
ViewerSpace(ViewManager& vm, Class cls, Space* parent) : Space(vm, cls, parent) {}
2016-02-01 12:04:55 -08:00
};
2018-12-07 21:30:43 -08:00
class EditorSpace : public Space {
2016-02-01 12:04:55 -08:00
public:
2018-12-07 21:30:43 -08:00
EditorSpace(ViewManager& vm, Class cls, Space* parent) : Space(vm, cls, parent) {}
2016-02-01 12:04:55 -08:00
};
2018-12-07 21:30:43 -08:00
class GameSpace : public Space {
public:
2018-12-07 21:30:43 -08:00
GameSpace(ViewManager& vm, Class cls, Space* parent) : Space(vm, cls, parent) {}
};
2018-12-07 21:30:43 -08:00
class TestSpace : public Space {
std::unique_ptr<specter::Button> m_button;
std::unique_ptr<specter::MultiLineTextView> m_textView;
2018-12-07 21:30:43 -08:00
std::string m_contentStr;
std::string m_buttonStr;
2018-12-07 21:30:43 -08:00
specter::IButtonBinding* m_binding;
public:
2018-12-07 21:30:43 -08:00
TestSpace(ViewManager& vm, Space* parent, std::string_view content, std::string_view button,
specter::IButtonBinding* binding)
: Space(vm, Class::TestSpace, parent), m_contentStr(content), m_buttonStr(button), m_binding(binding) {}
2019-08-10 17:49:41 -07:00
struct State : Space::State{AT_DECL_DNA_YAMLV} m_state;
const Space::State& spaceState() const override { return m_state; }
2018-12-07 21:30:43 -08:00
bool usesToolbar() const override { return true; }
void buildToolbarView(specter::ViewResources& res, specter::Toolbar& tb) override {
2018-12-07 21:30:43 -08:00
m_button.reset(new specter::Button(res, tb, m_binding, m_buttonStr));
tb.push_back(m_button.get(), 0);
}
specter::View* buildContentView(specter::ViewResources& res) override {
2018-12-07 21:30:43 -08:00
m_textView.reset(new specter::MultiLineTextView(res, *m_spaceView, res.m_heading14));
m_textView->setBackground(res.themeData().viewportBackground());
m_textView->typesetGlyphs(m_contentStr, res.themeData().uiText());
return m_textView.get();
}
};
2021-04-10 01:42:06 -07:00
} // namespace metaforce