EM_Task/UnrealEd/Classes/Editor/TransBuffer.h
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

266 lines
9.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/**
* Transaction tracking system, manages the undo and redo buffer.
*/
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Editor/Transactor.h"
#include "TransBuffer.generated.h"
UCLASS(transient)
class UNREALED_API UTransBuffer
: public UTransactor
{
public:
GENERATED_BODY()
// Variables.
/** The queue of transaction records */
TArray<TSharedRef<FTransaction>> UndoBuffer;
/** Number of transactions that have been undone, and are eligible to be redone */
int32 UndoCount;
/** Text describing the reason that the undo buffer is empty */
FText ResetReason;
/** Number of actions in the current transaction */
int32 ActiveCount;
/** The cached count of the number of object records each time a transaction is begun */
TArray<int32> ActiveRecordCounts;
/** Maximum number of bytes the transaction buffer is allowed to occupy */
SIZE_T MaxMemory;
/** Undo barrier stack */
TArray<int32> UndoBarrierStack;
public:
// Constructor.
void Initialize(SIZE_T InMaxMemory);
public:
/**
* Validates the state of the transaction buffer.
*/
void CheckState() const;
static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
public:
//~ Begin UObject Interface.
virtual void Serialize(FArchive& Ar) override;
virtual void FinishDestroy() override;
//~ End UObject Interface.
protected:
/** Implementation of the begin function. Used to create a specific transaction type */
template <typename TTransaction>
int32 BeginInternal(const TCHAR* SessionContext, const FText& Description)
{
int32 Result = INDEX_NONE;
CheckState();
if (ensure(!GIsTransacting))
{
Result = ActiveCount;
if (ActiveCount++ == 0)
{
// Redo removal handling
if (UndoCount > 0)
{
RemovedTransactions.Reserve(UndoCount);
for (int32 i = UndoBuffer.Num() - UndoCount; i < UndoBuffer.Num(); ++i)
{
RemovedTransactions.Add(UndoBuffer[i]);
}
UndoBuffer.RemoveAt(UndoBuffer.Num() - UndoCount, UndoCount, false);
}
// Over memory budget handling
else
{
int32 TransactionsToRemove = 0;
// Determine if any additional entries need to be removed due to memory, avoid n^2 by unrolling GetUndoSize()
SIZE_T AccumulatedBufferDataSize = GetUndoSize();
if (AccumulatedBufferDataSize >= MaxMemory)
{
// Then remove from the oldest one
for (int32 i = 0; i < UndoBuffer.Num() && AccumulatedBufferDataSize >= MaxMemory; i++)
{
if (AccumulatedBufferDataSize >= MaxMemory)
{
AccumulatedBufferDataSize -= UndoBuffer[i]->DataSize();
++TransactionsToRemove;
}
}
}
if (TransactionsToRemove > 0)
{
RemovedTransactions.Reserve(TransactionsToRemove);
for (int32 i = 0; i < TransactionsToRemove; ++i)
{
RemovedTransactions.Add(UndoBuffer[i]);
}
UndoBuffer.RemoveAt(0, TransactionsToRemove, false);
}
}
// Cache the redo buffer in case the transaction is canceled so we can restore the state
PreviousUndoCount = UndoCount;
UndoCount = 0;
// Begin a new transaction.
UndoBuffer.Emplace(MakeShareable(new TTransaction(SessionContext, Description, 1)));
GUndo = &UndoBuffer.Last().Get();
GUndo->BeginOperation();
TransactionStateChangedDelegate.Broadcast(GUndo->GetContext(), ETransactionStateEventType::TransactionStarted);
UndoBufferChangedDelegate.Broadcast();
}
const int32 PriorRecordsCount = (Result > 0 ? ActiveRecordCounts[Result - 1] : 0);
ActiveRecordCounts.Add(UndoBuffer.Last()->GetRecordCount() - PriorRecordsCount);
CheckState();
}
return Result;
}
public:
//~ Begin UTransactor Interface.
virtual int32 Begin(const TCHAR* SessionContext, const FText& Description) override;
virtual int32 End() override;
virtual void Cancel(int32 StartIndex = 0) override;
virtual void Reset(const FText& Reason) override;
virtual bool CanUndo(FText* Text = NULL) override;
virtual bool CanRedo(FText* Text = NULL) override;
virtual int32 GetQueueLength() const override { return UndoBuffer.Num(); }
virtual int32 FindTransactionIndex(const FGuid& TransactionId) const override;
virtual const FTransaction* GetTransaction(int32 QueueIndex) const override;
virtual FTransactionContext GetUndoContext(bool bCheckWhetherUndoPossible = true) override;
virtual SIZE_T GetUndoSize() const override;
virtual int32 GetUndoCount() const override { return UndoCount; }
virtual FTransactionContext GetRedoContext() override;
virtual void SetUndoBarrier() override;
virtual void RemoveUndoBarrier() override;
virtual void ClearUndoBarriers() override;
virtual bool Undo(bool bCanRedo = true) override;
virtual bool Redo() override;
virtual bool EnableObjectSerialization() override;
virtual bool DisableObjectSerialization() override;
virtual bool IsObjectSerializationEnabled() override { return DisallowObjectSerialization == 0; }
virtual void SetPrimaryUndoObject(UObject* Object) override;
virtual bool IsObjectInTransationBuffer(const UObject* Object) const override;
virtual bool IsObjectTransacting(const UObject* Object) const override;
virtual bool ContainsPieObjects() const override;
virtual bool IsActive() override
{
return ActiveCount > 0;
}
//~ End UTransactor Interface.
public:
/**
* Gets an event delegate that is executed when a transaction state changes.
*
* @return The event delegate.
*/
DECLARE_EVENT_TwoParams(UTransBuffer, FOnTransactorTransactionStateChanged, const FTransactionContext& /*TransactionContext*/, ETransactionStateEventType /*TransactionState*/)
FOnTransactorTransactionStateChanged& OnTransactionStateChanged()
{
return TransactionStateChangedDelegate;
}
/**
* Gets an event delegate that is executed when a redo operation is being attempted.
*
* @return The event delegate.
*
* @see OnUndo
*/
DECLARE_EVENT_OneParam(UTransBuffer, FOnTransactorBeforeRedoUndo, const FTransactionContext& /*TransactionContext*/)
FOnTransactorBeforeRedoUndo& OnBeforeRedoUndo()
{
return BeforeRedoUndoDelegate;
}
/**
* Gets an event delegate that is executed when a redo operation is being attempted.
*
* @return The event delegate.
*
* @see OnUndo
*/
DECLARE_EVENT_TwoParams(UTransBuffer, FOnTransactorRedo, const FTransactionContext& /*TransactionContext*/, bool /*Succeeded*/)
FOnTransactorRedo& OnRedo()
{
return RedoDelegate;
}
/**
* Gets an event delegate that is executed when a undo operation is being attempted.
*
* @return The event delegate.
*
* @see OnRedo
*/
DECLARE_EVENT_TwoParams(UTransBuffer, FOnTransactorUndo, const FTransactionContext& /*TransactionContext*/, bool /*Succeeded*/)
FOnTransactorUndo& OnUndo()
{
return UndoDelegate;
}
/**
* Gets an event delegate that is executed when the undo buffer changed.
*
* @return The event delegate.
*
* @see OnRedo
*/
DECLARE_EVENT(UTransBuffer, FOnTransactorUndoBufferChanged)
FOnTransactorUndoBufferChanged& OnUndoBufferChanged()
{
return UndoBufferChangedDelegate;
}
private:
/** Controls whether the transaction buffer is allowed to serialize object references */
int32 DisallowObjectSerialization;
private:
// Holds an event delegate that is executed when a transaction state changes.
FOnTransactorTransactionStateChanged TransactionStateChangedDelegate;
// Holds an event delegate that is executed before a redo or undo operation is attempted.
FOnTransactorBeforeRedoUndo BeforeRedoUndoDelegate;
// Holds an event delegate that is executed when a redo operation is being attempted.
FOnTransactorRedo RedoDelegate;
// Holds an event delegate that is executed when a undo operation is being attempted.
FOnTransactorUndo UndoDelegate;
// Holds an event delegate that is executed when the undo buffer changed.
FOnTransactorUndoBufferChanged UndoBufferChangedDelegate;
// Reference to the current transaction, nullptr when not transacting:
FTransaction* CurrentTransaction;
// Cached previous undo count while a transaction is being built in case we cancel it and want to restore the previous undo buffer
int32 PreviousUndoCount;
// The list of transactions that were removed when a transaction was begun in case it is canceled and we want to restore the original state
TArray<TSharedRef<FTransaction>> RemovedTransactions;
};