Added Undo Redo to editor
This commit is contained in:
172
OpenRA.Mods.Common/Traits/World/EditorActionManager.cs
Normal file
172
OpenRA.Mods.Common/Traits/World/EditorActionManager.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* 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
|
||||
{
|
||||
public class EditorActionManagerInfo : TraitInfo<EditorActionManager> { }
|
||||
|
||||
public class EditorActionManager : IWorldLoaded
|
||||
{
|
||||
readonly Stack<EditorActionContainer> undoStack = new Stack<EditorActionContainer>();
|
||||
readonly Stack<EditorActionContainer> redoStack = new Stack<EditorActionContainer>();
|
||||
|
||||
public event Action<EditorActionContainer> ItemAdded;
|
||||
public event Action<EditorActionContainer> ItemRemoved;
|
||||
public event Action OnChange;
|
||||
|
||||
int nextId;
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
Add(new OpenMapAction());
|
||||
}
|
||||
|
||||
public void Add(IEditorAction editorAction)
|
||||
{
|
||||
editorAction.Execute();
|
||||
|
||||
if (undoStack.Count > 0)
|
||||
undoStack.Peek().Status = EditorActionStatus.History;
|
||||
|
||||
var actionContainer = new EditorActionContainer(nextId++, editorAction);
|
||||
|
||||
ClearRedo();
|
||||
undoStack.Push(actionContainer);
|
||||
|
||||
if (ItemAdded != null)
|
||||
ItemAdded(actionContainer);
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
if (!HasUndos())
|
||||
return;
|
||||
|
||||
var editorAction = undoStack.Pop();
|
||||
undoStack.Peek().Status = EditorActionStatus.Active;
|
||||
editorAction.Action.Undo();
|
||||
editorAction.Status = EditorActionStatus.Future;
|
||||
redoStack.Push(editorAction);
|
||||
|
||||
if (OnChange != null)
|
||||
OnChange();
|
||||
}
|
||||
|
||||
void ClearRedo()
|
||||
{
|
||||
while (HasRedos())
|
||||
{
|
||||
var item = redoStack.Pop();
|
||||
|
||||
if (ItemRemoved != null)
|
||||
ItemRemoved(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
if (!HasRedos())
|
||||
return;
|
||||
|
||||
var editorAction = redoStack.Pop();
|
||||
|
||||
editorAction.Status = EditorActionStatus.Active;
|
||||
editorAction.Action.Do();
|
||||
undoStack.Peek().Status = EditorActionStatus.History;
|
||||
undoStack.Push(editorAction);
|
||||
|
||||
if (OnChange != null)
|
||||
OnChange();
|
||||
}
|
||||
|
||||
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 enum EditorActionStatus
|
||||
{
|
||||
History,
|
||||
Active,
|
||||
Future,
|
||||
}
|
||||
|
||||
public interface IEditorAction
|
||||
{
|
||||
void Execute();
|
||||
void Do();
|
||||
void Undo();
|
||||
|
||||
string Text { get; }
|
||||
}
|
||||
|
||||
class OpenMapAction : IEditorAction
|
||||
{
|
||||
public OpenMapAction()
|
||||
{
|
||||
Text = "Opened";
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
}
|
||||
|
||||
public void Do()
|
||||
{
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
}
|
||||
|
||||
public string Text { get; private set; }
|
||||
|
||||
public EditorActionStatus Status { get; set; }
|
||||
}
|
||||
|
||||
public class EditorActionContainer
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
public IEditorAction Action { get; private set; }
|
||||
public EditorActionStatus Status { get; set; }
|
||||
|
||||
public EditorActionContainer(int id, IEditorAction action)
|
||||
{
|
||||
Id = id;
|
||||
Action = action;
|
||||
Status = EditorActionStatus.Active;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,8 +110,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var owner = Players.Players[reference.InitDict.Get<OwnerInit>().PlayerName];
|
||||
|
||||
var preview = new EditorActorPreview(worldRenderer, id, reference, owner);
|
||||
previews.Add(preview);
|
||||
|
||||
Add(preview, initialSetup);
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
public void Add(EditorActorPreview preview, bool initialSetup = false)
|
||||
{
|
||||
previews.Add(preview);
|
||||
if (!preview.Bounds.IsEmpty)
|
||||
screenMap.Add(preview, preview.Bounds);
|
||||
|
||||
@@ -126,11 +133,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
UpdateNeighbours(preview.Footprint);
|
||||
|
||||
if (reference.Type == "mpspawn")
|
||||
if (preview.Actor.Type == "mpspawn")
|
||||
SyncMultiplayerCount();
|
||||
}
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
public void Remove(EditorActorPreview preview)
|
||||
@@ -262,7 +267,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
string NextActorName()
|
||||
{
|
||||
var id = previews.Count();
|
||||
var id = previews.Count;
|
||||
var possibleName = "Actor" + id.ToString();
|
||||
|
||||
while (previews.Any(p => p.ID == possibleName))
|
||||
|
||||
@@ -20,7 +20,7 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class EditorActorPreview
|
||||
public class EditorActorPreview : IEquatable<EditorActorPreview>
|
||||
{
|
||||
public readonly string DescriptiveName;
|
||||
public readonly ActorInfo Info;
|
||||
@@ -28,6 +28,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public readonly IReadOnlyDictionary<CPos, SubCell> Footprint;
|
||||
public readonly Rectangle Bounds;
|
||||
public readonly SelectionBoxRenderable SelectionBox;
|
||||
public readonly ActorReference Actor;
|
||||
|
||||
public string Tooltip
|
||||
{
|
||||
@@ -43,7 +44,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public SubCell SubCell { get; private set; }
|
||||
public bool Selected { get; set; }
|
||||
|
||||
readonly ActorReference actor;
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly TooltipInfoBase tooltip;
|
||||
IActorPreview[] previews;
|
||||
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public EditorActorPreview(WorldRenderer worldRenderer, string id, ActorReference actor, PlayerReference owner)
|
||||
{
|
||||
ID = id;
|
||||
this.actor = actor;
|
||||
Actor = actor;
|
||||
Owner = owner;
|
||||
this.worldRenderer = worldRenderer;
|
||||
|
||||
@@ -120,25 +120,25 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public void ReplaceInit<T>(T init)
|
||||
{
|
||||
var original = actor.InitDict.GetOrDefault<T>();
|
||||
var original = Actor.InitDict.GetOrDefault<T>();
|
||||
if (original != null)
|
||||
actor.InitDict.Remove(original);
|
||||
Actor.InitDict.Remove(original);
|
||||
|
||||
actor.InitDict.Add(init);
|
||||
Actor.InitDict.Add(init);
|
||||
GeneratePreviews();
|
||||
}
|
||||
|
||||
public void RemoveInit<T>()
|
||||
{
|
||||
var original = actor.InitDict.GetOrDefault<T>();
|
||||
var original = Actor.InitDict.GetOrDefault<T>();
|
||||
if (original != null)
|
||||
actor.InitDict.Remove(original);
|
||||
Actor.InitDict.Remove(original);
|
||||
GeneratePreviews();
|
||||
}
|
||||
|
||||
public T Init<T>()
|
||||
{
|
||||
return actor.InitDict.GetOrDefault<T>();
|
||||
return Actor.InitDict.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public MiniYaml Save()
|
||||
@@ -154,7 +154,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return true;
|
||||
};
|
||||
|
||||
return actor.Save(saveInit);
|
||||
return Actor.Save(saveInit);
|
||||
}
|
||||
|
||||
WPos PreviewPosition(World world, TypeDictionary init)
|
||||
@@ -167,7 +167,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var cell = init.Get<LocationInit>().Value(world);
|
||||
var offset = WVec.Zero;
|
||||
|
||||
var subCellInit = actor.InitDict.GetOrDefault<SubCellInit>();
|
||||
var subCellInit = Actor.InitDict.GetOrDefault<SubCellInit>();
|
||||
var subCell = subCellInit != null ? subCellInit.Value(worldRenderer.World) : SubCell.Any;
|
||||
|
||||
var buildingInfo = Info.TraitInfoOrDefault<BuildingInfo>();
|
||||
@@ -182,7 +182,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
void GeneratePreviews()
|
||||
{
|
||||
var init = new ActorPreviewInitializer(Info, worldRenderer, actor.InitDict);
|
||||
var init = new ActorPreviewInitializer(Info, worldRenderer, Actor.InitDict);
|
||||
previews = Info.TraitInfos<IRenderActorPreviewInfo>()
|
||||
.SelectMany(rpi => rpi.RenderPreview(init))
|
||||
.ToArray();
|
||||
@@ -190,12 +190,39 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public ActorReference Export()
|
||||
{
|
||||
return new ActorReference(actor.Type, actor.Save().ToDictionary());
|
||||
return new ActorReference(Actor.Type, Actor.Save().ToDictionary());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{0} {1}".F(Info.Name, ID);
|
||||
}
|
||||
|
||||
public bool Equals(EditorActorPreview other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
return false;
|
||||
if (ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
return string.Equals(ID, other.ID, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
return false;
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
if (obj.GetType() != GetType())
|
||||
return false;
|
||||
|
||||
return Equals((EditorActorPreview)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(ID) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user