Files
OpenRA/OpenRA.Mods.Common/Traits/World/EditorActionManager.cs
2024-11-03 16:52:47 +02:00

189 lines
3.6 KiB
C#

#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[TraitLocation(SystemActors.EditorWorld)]
public class EditorActionManagerInfo : TraitInfo<EditorActionManager> { }
public class EditorActionManager : IWorldLoaded
{
readonly Stack<EditorActionContainer> undoStack = new();
readonly Stack<EditorActionContainer> redoStack = new();
public event Action<EditorActionContainer> ItemAdded;
public event Action<EditorActionContainer> ItemRemoved;
public event Action OnChange;
int nextId;
public bool Modified;
public bool SaveFailed;
public void WorldLoaded(World w, WorldRenderer wr)
{
Add(new OpenMapAction());
Modified = false;
}
public void Add(IEditorAction editorAction)
{
Modified = true;
editorAction.Execute();
if (undoStack.Count > 0)
undoStack.Peek().Status = EditorActionStatus.History;
var actionContainer = new EditorActionContainer(nextId++, editorAction);
ClearRedo();
undoStack.Push(actionContainer);
ItemAdded?.Invoke(actionContainer);
}
public void Undo()
{
if (!HasUndos())
return;
Modified = true;
var editorAction = undoStack.Pop();
undoStack.Peek().Status = EditorActionStatus.Active;
editorAction.Action.Undo();
editorAction.Status = EditorActionStatus.Future;
redoStack.Push(editorAction);
OnChange?.Invoke();
}
void ClearRedo()
{
while (HasRedos())
{
var item = redoStack.Pop();
ItemRemoved?.Invoke(item);
}
}
public void Redo()
{
if (!HasRedos())
return;
Modified = true;
var editorAction = redoStack.Pop();
editorAction.Status = EditorActionStatus.Active;
editorAction.Action.Do();
undoStack.Peek().Status = EditorActionStatus.History;
undoStack.Push(editorAction);
OnChange?.Invoke();
}
public bool HasUndos()
{
// Preserve the initial OpenMapAction.
return undoStack.Count > 1;
}
public bool HasRedos()
{
return redoStack.Count > 0;
}
public void Rewind(int id)
{
while (undoStack.Peek().Id != id)
Undo();
}
public void Forward(int id)
{
while (undoStack.Peek().Id != id)
Redo();
}
public bool HasUnsavedItems()
{
// Modified and last action isn't the OpenMapAction (+ no redos)
return Modified && !(undoStack.Peek().Action is OpenMapAction && !HasRedos());
}
}
public enum EditorActionStatus
{
History,
Active,
Future,
}
public interface IEditorAction
{
void Execute();
void Do();
void Undo();
string Text { get; }
}
sealed class OpenMapAction : IEditorAction
{
[FluentReference]
const string Opened = "notification-opened";
public OpenMapAction()
{
Text = FluentProvider.GetMessage(Opened);
}
public void Execute()
{
Do();
}
public void Do()
{
}
public void Undo()
{
}
public string Text { get; }
public EditorActionStatus Status { get; set; }
}
public class EditorActionContainer
{
public int Id { get; }
public IEditorAction Action { get; }
public EditorActionStatus Status { get; set; }
public EditorActionContainer(int id, IEditorAction action)
{
Id = id;
Action = action;
Status = EditorActionStatus.Active;
}
}
}