Add ActionStack and automatically register actions in menubar
This commit is contained in:
parent
b5a750349c
commit
9b9b93a183
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <NazaraEditor/Core/Core.hpp>
|
#include <NazaraEditor/Core/Core.hpp>
|
||||||
#include <NazaraEditor/Core/Application/Action.hpp>
|
#include <NazaraEditor/Core/Application/Action.hpp>
|
||||||
|
#include <NazaraEditor/Core/Application/ActionStack.hpp>
|
||||||
#include <NazaraEditor/Core/Application/BaseApplication.hpp>
|
#include <NazaraEditor/Core/Application/BaseApplication.hpp>
|
||||||
#include <NazaraEditor/Core/Application/EditorLogger.hpp>
|
#include <NazaraEditor/Core/Application/EditorLogger.hpp>
|
||||||
#include <NazaraEditor/Core/Application/Level.hpp>
|
#include <NazaraEditor/Core/Application/Level.hpp>
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,31 @@
|
||||||
|
|
||||||
#include <Nazara/Renderer.hpp>
|
#include <Nazara/Renderer.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
class EditorAction
|
class NAZARAEDITOR_CORE_API EditorAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Properties
|
struct Properties
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string className;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
std::string path;
|
||||||
|
std::string category;
|
||||||
Nz::Shortcut shortcut;
|
Nz::Shortcut shortcut;
|
||||||
|
|
||||||
Nz::Texture* icon;
|
std::shared_ptr<Nz::Texture> icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
EditorAction(const Properties& properties)
|
EditorAction(const Properties& properties)
|
||||||
: m_properties(std::make_shared<Properties>(properties))
|
: m_properties(std::make_shared<Properties>(properties))
|
||||||
{}
|
{}
|
||||||
|
EditorAction(const std::shared_ptr<Properties>&properties)
|
||||||
|
: m_properties(properties)
|
||||||
|
{}
|
||||||
virtual ~EditorAction() = default;
|
virtual ~EditorAction() = default;
|
||||||
|
|
||||||
EditorAction(const EditorAction&) = delete;
|
EditorAction(const EditorAction&) = delete;
|
||||||
|
|
@ -32,15 +38,35 @@ namespace Nz
|
||||||
EditorAction(EditorAction&&) = delete;
|
EditorAction(EditorAction&&) = delete;
|
||||||
EditorAction& operator=(EditorAction&&) = delete;
|
EditorAction& operator=(EditorAction&&) = delete;
|
||||||
|
|
||||||
virtual void Execute() = 0;
|
virtual std::unique_ptr<EditorAction> Clone() const = 0;
|
||||||
virtual void Revert() = 0;
|
virtual const std::string& GetName() const = 0;
|
||||||
virtual EditorAction* Clone() const = 0;
|
virtual bool IsUndoRedoable() const = 0;
|
||||||
|
static const char* GetClassName() { return "EditorAction"; }
|
||||||
|
|
||||||
|
virtual void Execute() {};
|
||||||
|
virtual void Revert() {};
|
||||||
|
|
||||||
|
template <typename TAction>
|
||||||
|
TAction* As() { return static_cast<TAction*>(this); }
|
||||||
|
|
||||||
|
template <typename TAction>
|
||||||
|
const TAction* As() const { return static_cast<const TAction*>(this); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EditorAction(const std::shared_ptr<Properties>& properties)
|
|
||||||
: m_properties(properties)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::shared_ptr<Properties> m_properties;
|
std::shared_ptr<Properties> m_properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define EDITORACTION_BODY(Typename, bUndoRedoable) \
|
||||||
|
public: \
|
||||||
|
Typename(const Properties& properties) \
|
||||||
|
: EditorAction(properties) \
|
||||||
|
{} \
|
||||||
|
Typename(const std::shared_ptr<Properties>& properties) \
|
||||||
|
: EditorAction(properties) \
|
||||||
|
{} \
|
||||||
|
~Typename() = default; \
|
||||||
|
std::unique_ptr<EditorAction> Clone() const override { return std::make_unique<Typename>(m_properties); } \
|
||||||
|
const std::string& GetName() const override { return m_properties->className; } \
|
||||||
|
bool IsUndoRedoable() const override { return bUndoRedoable; } \
|
||||||
|
static const char* GetClassName() { return #Typename; }
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <NazaraEditor/Core/Config.hpp>
|
||||||
|
#include <NazaraEditor/Core/Application/Action.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
class NAZARAEDITOR_CORE_API ActionStack final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static ActionStack* Instance();
|
||||||
|
|
||||||
|
void ExecuteAction(const std::string& name)
|
||||||
|
{
|
||||||
|
ExecuteAction(CreateAction(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecuteAction(const std::shared_ptr<EditorAction>& action)
|
||||||
|
{
|
||||||
|
if (!action)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!action->IsUndoRedoable())
|
||||||
|
{
|
||||||
|
action->Execute();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CanRedo())
|
||||||
|
{
|
||||||
|
// erase stack content after current index
|
||||||
|
m_undoRedoStack.resize(m_currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_undoRedoStack.push_back(std::move(action));
|
||||||
|
Redo();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<EditorAction> CreateAction(const std::string& name)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_availableActions.begin(), m_availableActions.end(), [&name](auto&& action) { return action->GetName() == name; });
|
||||||
|
if (it == m_availableActions.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return std::shared_ptr<EditorAction>((*it)->Clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Undo();
|
||||||
|
bool CanUndo() const;
|
||||||
|
|
||||||
|
void Redo();
|
||||||
|
bool CanRedo() const;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ActionStack();
|
||||||
|
~ActionStack();
|
||||||
|
|
||||||
|
ActionStack(ActionStack&) = delete;
|
||||||
|
ActionStack& operator=(ActionStack&) = delete;
|
||||||
|
ActionStack(ActionStack&&) = delete;
|
||||||
|
ActionStack& operator=(ActionStack&&) = delete;
|
||||||
|
|
||||||
|
template <typename TAction>
|
||||||
|
void RegisterAction(const EditorAction::Properties& properties)
|
||||||
|
{
|
||||||
|
m_availableActions.push_back(std::make_unique<TAction>(properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ActionStack* s_instance;
|
||||||
|
|
||||||
|
int64_t m_currentIndex;
|
||||||
|
std::vector<std::shared_ptr<EditorAction>> m_undoRedoStack;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<EditorAction>> m_availableActions;
|
||||||
|
|
||||||
|
friend class EditorBaseApplication;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <NazaraEditor/Core/Core.hpp>
|
#include <NazaraEditor/Core/Core.hpp>
|
||||||
#include <NazaraEditor/Core/Application/Action.hpp>
|
#include <NazaraEditor/Core/Application/Action.hpp>
|
||||||
|
#include <NazaraEditor/Core/Application/ActionStack.hpp>
|
||||||
#include <NazaraEditor/Core/Application/Level.hpp>
|
#include <NazaraEditor/Core/Application/Level.hpp>
|
||||||
#include <NazaraEditor/Core/UI/Window.hpp>
|
#include <NazaraEditor/Core/UI/Window.hpp>
|
||||||
#include <NazaraImgui/NazaraImgui.hpp>
|
#include <NazaraImgui/NazaraImgui.hpp>
|
||||||
|
|
@ -29,6 +30,8 @@ namespace Nz
|
||||||
NazaraSignal(OnEntitySelected, entt::handle);
|
NazaraSignal(OnEntitySelected, entt::handle);
|
||||||
NazaraSignal(OnEntityDeselected, entt::handle);
|
NazaraSignal(OnEntityDeselected, entt::handle);
|
||||||
|
|
||||||
|
// Editor events
|
||||||
|
NazaraSignal(OnActionRegistered, const EditorAction::Properties&);
|
||||||
EditorBaseApplication();
|
EditorBaseApplication();
|
||||||
virtual ~EditorBaseApplication() = default;
|
virtual ~EditorBaseApplication() = default;
|
||||||
|
|
||||||
|
|
@ -46,11 +49,19 @@ namespace Nz
|
||||||
m_windows.push_back(std::make_unique<T>(this));
|
m_windows.push_back(std::make_unique<T>(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TAction>
|
||||||
|
void RegisterAction(EditorAction::Properties properties)
|
||||||
|
{
|
||||||
|
properties.className = TAction::GetClassName();
|
||||||
|
m_actionStack.RegisterAction<TAction>(properties);
|
||||||
|
OnActionRegistered(properties);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Nz::WindowSwapchain> m_windowSwapchain;
|
std::unique_ptr<Nz::WindowSwapchain> m_windowSwapchain;
|
||||||
std::vector<std::unique_ptr<Nz::EditorWindow>> m_windows;
|
std::vector<std::unique_ptr<Nz::EditorWindow>> m_windows;
|
||||||
std::vector<std::unique_ptr<EditorAction>> m_actions;
|
|
||||||
|
|
||||||
|
Nz::ActionStack m_actionStack;
|
||||||
Nz::Level m_level;
|
Nz::Level m_level;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include <NazaraEditor/Core/Application/ActionStack.hpp>
|
||||||
|
|
||||||
|
namespace Nz
|
||||||
|
{
|
||||||
|
ActionStack* ActionStack::s_instance = nullptr;
|
||||||
|
ActionStack::ActionStack()
|
||||||
|
: m_currentIndex(0)
|
||||||
|
{
|
||||||
|
NazaraAssert(s_instance == nullptr, "ActionStack already exists");
|
||||||
|
s_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionStack::~ActionStack()
|
||||||
|
{
|
||||||
|
s_instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionStack* ActionStack::Instance()
|
||||||
|
{
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ActionStack::CanUndo() const { return !m_undoRedoStack.empty() && m_currentIndex >= 0; }
|
||||||
|
void ActionStack::Undo()
|
||||||
|
{
|
||||||
|
if (!CanUndo())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_undoRedoStack[m_currentIndex--]->Revert();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActionStack::CanRedo() const { return !m_undoRedoStack.empty() && m_currentIndex < m_undoRedoStack.size(); }
|
||||||
|
void ActionStack::Redo()
|
||||||
|
{
|
||||||
|
if (!CanRedo())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_undoRedoStack[m_currentIndex++]->Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,10 @@ namespace Nz
|
||||||
, m_windowName(name)
|
, m_windowName(name)
|
||||||
{
|
{
|
||||||
Nz::Imgui::Instance()->AddHandler(this);
|
Nz::Imgui::Instance()->AddHandler(this);
|
||||||
|
app->OnActionRegistered.Connect([this](auto&& prop) {
|
||||||
|
auto name = prop.className;
|
||||||
|
AddMenuAction(prop.path, prop.shortcut.ToString(), [name]() { Nz::ActionStack::Instance()->ExecuteAction(name); }, prop.icon);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorWindow::~EditorWindow()
|
EditorWindow::~EditorWindow()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue