Added Undo Redo to editor

This commit is contained in:
teinarss
2019-07-13 18:21:01 +02:00
committed by abcdefg30
parent 1f78b3a425
commit 76034c198e
19 changed files with 1155 additions and 164 deletions

View File

@@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly WorldRenderer worldRenderer;
readonly EditorActorLayer editorActorLayer;
readonly EditorActionManager editorActionManager;
readonly EditorViewportControllerWidget editor;
readonly BackgroundWidget actorEditPanel;
readonly LabelWidget typeLabel;
@@ -48,6 +49,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
string initialActorID;
EditorActorPreview currentActorInner;
EditActorPreview editActorPreview;
EditorActorPreview CurrentActor
{
get
@@ -61,7 +64,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return;
if (currentActorInner != null)
{
Reset();
currentActorInner.Selected = false;
}
currentActorInner = value;
if (currentActorInner != null)
@@ -74,6 +80,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
this.worldRenderer = worldRenderer;
editorActorLayer = world.WorldActor.Trait<EditorActorLayer>();
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
editor = widget.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
actorEditPanel = editor.Get<BackgroundWidget>("ACTOR_EDIT_PANEL");
@@ -88,7 +96,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
initContainer.RemoveChildren();
var deleteButton = actorEditPanel.Get<ButtonWidget>("DELETE_BUTTON");
var closeButton = actorEditPanel.Get<ButtonWidget>("CLOSE_BUTTON");
var cancelButton = actorEditPanel.Get<ButtonWidget>("CANCEL_BUTTON");
var okButton = actorEditPanel.Get<ButtonWidget>("OK_BUTTON");
actorIDErrorLabel = actorEditPanel.Get<LabelWidget>("ACTOR_ID_ERROR_LABEL");
actorIDErrorLabel.IsVisible = () => actorIDStatus != ActorIDStatus.Normal;
@@ -99,7 +108,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (logicArgs.TryGetValue("EditPanelPadding", out yaml))
editPanelPadding = FieldLoader.GetValue<int>("EditPanelPadding", yaml.Value);
closeButton.OnClick = Close;
okButton.IsDisabled = () => !IsValid() || !editActorPreview.IsDirty;
okButton.OnClick = Save;
cancelButton.OnClick = Cancel;
deleteButton.OnClick = Delete;
actorEditPanel.IsVisible = () => CurrentActor != null
&& editor.CurrentBrush == editor.DefaultBrush
@@ -113,15 +124,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
actorIDField.OnTextEdited = () =>
{
if (string.IsNullOrWhiteSpace(actorIDField.Text))
var actorId = actorIDField.Text.Trim();
if (string.IsNullOrWhiteSpace(actorId))
{
nextActorIDStatus = ActorIDStatus.Empty;
return;
}
// Check for duplicate actor ID
var actorId = actorIDField.Text.ToLowerInvariant();
if (CurrentActor.ID.ToLowerInvariant() != actorId)
if (CurrentActor.ID.Equals(actorId, StringComparison.OrdinalIgnoreCase))
{
if (editorActorLayer[actorId] != null)
{
@@ -130,23 +141,30 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
}
SetActorID(world, actorId);
SetActorID(actorId);
nextActorIDStatus = ActorIDStatus.Normal;
};
actorIDField.OnLoseFocus = () =>
{
// Reset invalid IDs back to their starting value
if (actorIDStatus != ActorIDStatus.Normal)
SetActorID(world, initialActorID);
SetActorID(initialActorID);
};
}
void SetActorID(World world, string actorId)
void SetActorID(string actorId)
{
CurrentActor.ID = actorId;
editActorPreview.SetActorID(actorId);
nextActorIDStatus = ActorIDStatus.Normal;
}
bool IsValid()
{
return nextActorIDStatus == ActorIDStatus.Normal;
}
public override void Tick()
{
if (actorIDStatus != nextActorIDStatus)
@@ -183,6 +201,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
lastScrollTime = 0; // Ensure visible
CurrentActor = actor;
editActorPreview = new EditActorPreview(CurrentActor);
initialActorID = actorIDField.Text = actor.ID;
var font = Game.Renderer.Fonts[typeLabel.Font];
@@ -203,13 +223,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var ownerDropdown = ownerContainer.Get<DropDownButtonWidget>("OPTION");
var selectedOwner = actor.Owner;
Action<EditorActorPreview, PlayerReference> updateOwner = (preview, reference) =>
{
preview.Owner = reference;
preview.ReplaceInit(new OwnerInit(reference.Name));
};
var ownerHandler = new EditorActorOptionActionHandle<PlayerReference>(updateOwner, actor.Owner);
editActorPreview.Add(ownerHandler);
Func<PlayerReference, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
{
var item = ScrollItemWidget.Setup(template, () => selectedOwner == option, () =>
{
selectedOwner = option;
CurrentActor.Owner = selectedOwner;
CurrentActor.ReplaceInit(new OwnerInit(selectedOwner.Name));
updateOwner(CurrentActor, selectedOwner);
ownerHandler.OnChange(option);
});
item.Get<LabelWidget>("LABEL").GetText = () => option.Name;
@@ -248,8 +277,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
slider.MaximumValue = so.MaxValue;
slider.Ticks = so.Ticks;
var editorActionHandle = new EditorActorOptionActionHandle<float>(so.OnChange, so.GetValue(actor));
editActorPreview.Add(editorActionHandle);
slider.GetValue = () => so.GetValue(actor);
slider.OnChange += value => so.OnChange(actor, value);
slider.OnChange += value => editorActionHandle.OnChange(value);
initContainer.AddChild(sliderContainer);
}
@@ -261,12 +294,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
initContainer.Bounds.Height += dropdownContainer.Bounds.Height;
dropdownContainer.Get<LabelWidget>("LABEL").GetText = () => ddo.Name;
var editorActionHandle = new EditorActorOptionActionHandle<string>(ddo.OnChange, ddo.GetValue(actor));
editActorPreview.Add(editorActionHandle);
var dropdown = dropdownContainer.Get<DropDownButtonWidget>("OPTION");
Func<KeyValuePair<string, string>, ScrollItemWidget, ScrollItemWidget> dropdownSetup = (option, template) =>
{
var item = ScrollItemWidget.Setup(template,
() => ddo.GetValue(actor) == option.Key,
() => ddo.OnChange(actor, option.Key));
() =>
{
ddo.OnChange(actor, option.Key);
editorActionHandle.OnChange(option.Key);
});
item.Get<LabelWidget>("LABEL").GetText = () => option.Value;
return item;
@@ -298,16 +338,180 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void Delete()
{
if (CurrentActor != null)
editorActorLayer.Remove(CurrentActor);
editorActionManager.Add(new RemoveActorAction(editorActorLayer, CurrentActor));
Close();
}
void Cancel()
{
Reset();
Close();
}
void Reset()
{
if (editActorPreview != null)
editActorPreview.Reset();
}
void Close()
{
actorIDField.YieldKeyboardFocus();
editor.DefaultBrush.SelectedActor = null;
CurrentActor = null;
}
void Save()
{
editorActionManager.Add(new EditActorEditorAction(editorActorLayer, CurrentActor, editActorPreview.GetDirtyHandles()));
editActorPreview = null;
Close();
}
}
public class EditorActorOptionActionHandle<T> : IEditActorHandle
{
readonly Action<EditorActorPreview, T> change;
T value;
readonly T initialValue;
public EditorActorOptionActionHandle(Action<EditorActorPreview, T> change, T value)
{
this.change = change;
this.value = value;
initialValue = value;
}
public void OnChange(T value)
{
IsDirty = !EqualityComparer<T>.Default.Equals(initialValue, value);
this.value = value;
}
public void Do(EditorActorPreview actor)
{
change(actor, value);
}
public void Undo(EditorActorPreview actor)
{
change(actor, initialValue);
}
public bool IsDirty { get; private set; }
}
public interface IEditActorHandle
{
void Do(EditorActorPreview actor);
void Undo(EditorActorPreview actor);
bool IsDirty { get; }
}
class EditActorEditorAction : IEditorAction
{
public string Text { get; private set; }
readonly IEnumerable<IEditActorHandle> handles;
readonly EditorActorLayer editorActorLayer;
EditorActorPreview actor;
readonly string actorId;
public EditActorEditorAction(EditorActorLayer editorActorLayer, EditorActorPreview actor, IEnumerable<IEditActorHandle> handles)
{
this.editorActorLayer = editorActorLayer;
actorId = actor.ID;
this.actor = actor;
this.handles = handles;
Text = "Edited {0} ({1})".F(actor.Info.Name, actor.ID);
}
public void Execute()
{
}
public void Do()
{
actor = editorActorLayer[actorId.ToLowerInvariant()];
foreach (var editorActionHandle in handles)
editorActionHandle.Do(actor);
}
public void Undo()
{
foreach (var editorActionHandle in handles)
editorActionHandle.Undo(actor);
}
}
class EditActorPreview
{
readonly EditorActorPreview actor;
readonly SetActorIdAction setActorIdAction;
readonly List<IEditActorHandle> handles = new List<IEditActorHandle>();
public EditActorPreview(EditorActorPreview actor)
{
this.actor = actor;
setActorIdAction = new SetActorIdAction(actor.ID);
handles.Add(setActorIdAction);
}
public bool IsDirty
{
get { return handles.Any(h => h.IsDirty); }
}
public void SetActorID(string actorID)
{
setActorIdAction.Set(actorID);
}
public void Add(IEditActorHandle editActor)
{
handles.Add(editActor);
}
public IEnumerable<IEditActorHandle> GetDirtyHandles()
{
return handles.Where(h => h.IsDirty);
}
public void Reset()
{
foreach (var handle in handles.Where(h => h.IsDirty))
handle.Undo(actor);
}
}
public class SetActorIdAction : IEditActorHandle
{
readonly string initial;
string newID;
public void Set(string actorId)
{
IsDirty = initial != actorId;
newID = actorId;
}
public SetActorIdAction(string initial)
{
this.initial = initial;
}
public void Do(EditorActorPreview actor)
{
actor.ID = newID;
}
public void Undo(EditorActorPreview actor)
{
actor.ID = initial;
}
public bool IsDirty { get; private set; }
}
}