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/Application/Action.hpp>
|
||||
#include <NazaraEditor/Core/Application/ActionStack.hpp>
|
||||
#include <NazaraEditor/Core/Application/BaseApplication.hpp>
|
||||
#include <NazaraEditor/Core/Application/EditorLogger.hpp>
|
||||
#include <NazaraEditor/Core/Application/Level.hpp>
|
||||
|
|
|
|||
|
|
@ -6,25 +6,31 @@
|
|||
|
||||
#include <Nazara/Renderer.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class EditorAction
|
||||
class NAZARAEDITOR_CORE_API EditorAction
|
||||
{
|
||||
public:
|
||||
struct Properties
|
||||
{
|
||||
std::string name;
|
||||
std::string className;
|
||||
std::string description;
|
||||
std::string path;
|
||||
std::string category;
|
||||
Nz::Shortcut shortcut;
|
||||
|
||||
Nz::Texture* icon;
|
||||
std::shared_ptr<Nz::Texture> icon;
|
||||
};
|
||||
|
||||
EditorAction(const Properties& properties)
|
||||
: m_properties(std::make_shared<Properties>(properties))
|
||||
{}
|
||||
EditorAction(const std::shared_ptr<Properties>&properties)
|
||||
: m_properties(properties)
|
||||
{}
|
||||
virtual ~EditorAction() = default;
|
||||
|
||||
EditorAction(const EditorAction&) = delete;
|
||||
|
|
@ -32,15 +38,35 @@ namespace Nz
|
|||
EditorAction(EditorAction&&) = delete;
|
||||
EditorAction& operator=(EditorAction&&) = delete;
|
||||
|
||||
virtual void Execute() = 0;
|
||||
virtual void Revert() = 0;
|
||||
virtual EditorAction* Clone() const = 0;
|
||||
virtual std::unique_ptr<EditorAction> Clone() const = 0;
|
||||
virtual const std::string& GetName() 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:
|
||||
EditorAction(const std::shared_ptr<Properties>& properties)
|
||||
: m_properties(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/Application/Action.hpp>
|
||||
#include <NazaraEditor/Core/Application/ActionStack.hpp>
|
||||
#include <NazaraEditor/Core/Application/Level.hpp>
|
||||
#include <NazaraEditor/Core/UI/Window.hpp>
|
||||
#include <NazaraImgui/NazaraImgui.hpp>
|
||||
|
|
@ -29,6 +30,8 @@ namespace Nz
|
|||
NazaraSignal(OnEntitySelected, entt::handle);
|
||||
NazaraSignal(OnEntityDeselected, entt::handle);
|
||||
|
||||
// Editor events
|
||||
NazaraSignal(OnActionRegistered, const EditorAction::Properties&);
|
||||
EditorBaseApplication();
|
||||
virtual ~EditorBaseApplication() = default;
|
||||
|
||||
|
|
@ -46,11 +49,19 @@ namespace Nz
|
|||
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:
|
||||
std::unique_ptr<Nz::WindowSwapchain> m_windowSwapchain;
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue