Add ActionStack and automatically register actions in menubar

This commit is contained in:
SweetId 2023-10-16 19:31:59 -04:00
parent b5a750349c
commit 9b9b93a183
6 changed files with 176 additions and 11 deletions

View File

@ -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>

View File

@ -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; }
}

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -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();
}
}

View File

@ -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()