You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzToolsFramework/AzToolsFramework/Undo/UndoSystem.h

209 lines
6.8 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef UNDO_SYSTEM_H
#define UNDO_SYSTEM_H
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#pragma once
namespace AzToolsFramework
{
namespace UndoSystem
{
class UndoStack;
typedef AZ::u64 URCommandID;
// plug one of these into your undo stack if you'd like notification.
class IUndoNotify
{
public:
virtual ~IUndoNotify() = default;
virtual void OnUndoStackChanged() = 0;
};
class URSequencePoint
{
public:
AZ_RTTI(URSequencePoint, "{D6A52DA5-DF44-43BE-B42C-B6E88BDF476A}")
AZ_CLASS_ALLOCATOR(URSequencePoint, AZ::SystemAllocator, 0)
typedef AZStd::vector<URSequencePoint*> ChildVec;
/**
Usage: construct a standalone command which can both implement undo+redo and have children commands
*/
explicit URSequencePoint(const AZStd::string& friendlyName, URCommandID id = 0);
/**
Usage: construct a child command which can both implement undo+redo and have children commands
plus automatically create a friendly child name and append to the parent's children
NB: Parent takes ownership of the allocation
*/
explicit URSequencePoint(URCommandID id);
/**
Usage: base implementation automatically deletes all children recursively
therefore only a topmost parent needs to be deleted from outside
*/
virtual ~URSequencePoint();
void RunUndo();
void RunRedo();
/**
Usage: override with class specific actions
*/
virtual void Undo();
virtual void Redo();
/**
Usage: override with class specific change comparison between undo/redo state.
This allows the undo system to remove commands that have no actual effect
(Eg: a command that changes a value from 5 to 5 has no effect and can be removed)
*/
virtual bool Changed() const = 0;
/**
Usage: return the first command in the parent/child tree with a matching id
returns NULL on failure to make any match
*/
URSequencePoint* Find(URCommandID id, const AZ::Uuid& typeOfCommand);
void SetName(const AZStd::string& friendlyName);
AZStd::string& GetName();
void SetParent(URSequencePoint* parent);
URSequencePoint* GetParent() const { return m_parent; }
const ChildVec& GetChildren() const { return m_children; }
bool HasRealChildren() const;
/**
Usage: pass a function callback that eats a URSequencePoint*
this walks the child tree and applies the callback to each URSequencePoint
// example
// commandPtr->ApplyToTree( AZStd::bind(&MyClass::DoSomethingWithCommandPtr, this, AZStd::placeholders::_1) );
*/
typedef AZStd::function< void(URSequencePoint*) > ApplyOperationCB;
void ApplyToTree(const ApplyOperationCB& applyCB);
bool IsPosted() const { return m_isPosted; }
// default id 0 is never equal, commands are only equal if IDs otherwise match
bool operator==(const URCommandID id) const { return m_id == 0 ? false : m_id == id; }
bool operator==(const URSequencePoint* com) const { return *this == com->m_id; }
friend UndoStack;
protected:
void AddChild(URSequencePoint*);
void RemoveChild(URSequencePoint*);
AZStd::string m_friendlyName;
URCommandID m_id;
ChildVec m_children;
URSequencePoint* m_parent;
bool m_isPosted;
};
/**
* Used by batch undo commands for dummy/batch/parent nodes where we
* know nothing will have changed.
*/
class BatchCommand
: public URSequencePoint
{
public:
AZ_RTTI(BatchCommand, "{3CA8855C-C6A5-4395-9B47-D3F5A13EFB2D}", URSequencePoint)
AZ_CLASS_ALLOCATOR(BatchCommand, AZ::SystemAllocator, 0)
explicit BatchCommand(const AZStd::string& friendlyName, URCommandID id = 0)
: URSequencePoint(friendlyName, id) {}
explicit BatchCommand(URCommandID id)
: URSequencePoint(id) {}
bool Changed() const override { return false; }
};
//--------------------------------------------------------------------
class UndoStack
{
public:
AZ_CLASS_ALLOCATOR(UndoStack, AZ::SystemAllocator, 0);
UndoStack(IUndoNotify* notify);
UndoStack(int /*no longer used*/, IUndoNotify* notify);
~UndoStack();
URSequencePoint* Post(URSequencePoint*);
URSequencePoint* Undo();
URSequencePoint* Redo();
/**
Usage: return the first command in all active command trees with a matching id
returns NULL on failure to make any match
*/
URSequencePoint* Find(URCommandID id, const AZ::Uuid& typeOfCommand);
// Moves the clean state to the current point.
// does not slice, you can undo back past the clean point.
void SetClean();
bool IsClean() const;
void Reset(); // clear everything
bool CanUndo() const;
bool CanRedo() const;
const char* GetRedoName() const;
const char* GetUndoName() const;
// get the sequence point that would be undone right now if you hit undo.
URSequencePoint* GetTop();
URSequencePoint* PopTop(); // by doing this, you take ownership of the memory.
/**
Usage: slices off all commands above the current cursor
example: undo followed by new commands should slice to remove redo commands no longer viable
*/
void Slice();
protected:
#ifdef _DEBUG
void CleanCheck();
#endif
int m_Cursor;
int m_CleanPoint;
typedef AZStd::vector<URSequencePoint*> SequencePointBuffer;
SequencePointBuffer m_SequencePointsBuffer;
IUndoNotify* m_notify;
private:
// undo operations are not reentrant
volatile bool reentryGuard;
};
}
} // namespace AzToolsFramework
#endif