Editor selection refactor pt1
This commit is contained in:
35
OpenRA.Mods.Common/EditorBrushes/EditorClipboard.cs
Normal file
35
OpenRA.Mods.Common/EditorBrushes/EditorClipboard.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.EditorBrushes
|
||||
{
|
||||
public readonly struct ClipboardTile
|
||||
{
|
||||
public readonly TerrainTile TerrainTile;
|
||||
public readonly ResourceTile ResourceTile;
|
||||
public readonly ResourceLayerContents ResourceLayerContents;
|
||||
public readonly byte Height;
|
||||
|
||||
public ClipboardTile(TerrainTile terrainTile, ResourceTile resourceTile, ResourceLayerContents resourceLayerContents, byte height)
|
||||
{
|
||||
TerrainTile = terrainTile;
|
||||
ResourceTile = resourceTile;
|
||||
ResourceLayerContents = resourceLayerContents;
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct EditorClipboard
|
||||
{
|
||||
public readonly CellRegion CellRegion;
|
||||
public readonly Dictionary<string, EditorActorPreview> Actors;
|
||||
public readonly Dictionary<CPos, ClipboardTile> Tiles;
|
||||
|
||||
public EditorClipboard(CellRegion cellRegion, Dictionary<string, EditorActorPreview> actors, Dictionary<CPos, ClipboardTile> tiles)
|
||||
{
|
||||
CellRegion = cellRegion;
|
||||
Actors = actors;
|
||||
Tiles = tiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.EditorBrushes;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
@@ -29,29 +30,33 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
public sealed class EditorCopyPasteBrush : IEditorBrush
|
||||
{
|
||||
enum State { SelectFirst, SelectSecond, Paste }
|
||||
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly EditorViewportControllerWidget editorWidget;
|
||||
readonly EditorSelectionLayer selectionLayer;
|
||||
readonly EditorActorLayer editorLayer;
|
||||
readonly Func<MapCopyFilters> getCopyFilters;
|
||||
readonly EditorActorLayer editorActorLayer;
|
||||
readonly EditorActionManager editorActionManager;
|
||||
readonly EditorClipboard clipboard;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
readonly Func<MapCopyFilters> getCopyFilters;
|
||||
|
||||
State state;
|
||||
CPos start;
|
||||
CPos end;
|
||||
public CPos? PastePreviewPosition { get; private set; }
|
||||
|
||||
public EditorCopyPasteBrush(EditorViewportControllerWidget editorWidget, WorldRenderer wr, Func<MapCopyFilters> getCopyFilters)
|
||||
public CellRegion Region => clipboard.CellRegion;
|
||||
|
||||
public EditorCopyPasteBrush(
|
||||
EditorViewportControllerWidget editorWidget,
|
||||
WorldRenderer wr,
|
||||
EditorClipboard clipboard,
|
||||
IResourceLayer resourceLayer,
|
||||
Func<MapCopyFilters> getCopyFilters)
|
||||
{
|
||||
this.getCopyFilters = getCopyFilters;
|
||||
this.editorWidget = editorWidget;
|
||||
this.clipboard = clipboard;
|
||||
this.resourceLayer = resourceLayer;
|
||||
worldRenderer = wr;
|
||||
|
||||
editorActionManager = wr.World.WorldActor.Trait<EditorActionManager>();
|
||||
|
||||
selectionLayer = wr.World.WorldActor.Trait<EditorSelectionLayer>();
|
||||
editorLayer = wr.World.WorldActor.Trait<EditorActorLayer>();
|
||||
this.getCopyFilters = getCopyFilters;
|
||||
editorActorLayer = wr.World.WorldActor.Trait<EditorActorLayer>();
|
||||
}
|
||||
|
||||
public bool HandleMouseInput(MouseInput mi)
|
||||
@@ -71,106 +76,31 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mi.Button == MouseButton.Left && (mi.Event == MouseInputEvent.Up || mi.Event == MouseInputEvent.Down))
|
||||
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
|
||||
{
|
||||
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
|
||||
switch (state)
|
||||
{
|
||||
case State.SelectFirst:
|
||||
if (mi.Event != MouseInputEvent.Down)
|
||||
break;
|
||||
start = cell;
|
||||
selectionLayer.SetCopyRegion(start, end);
|
||||
state = State.SelectSecond;
|
||||
break;
|
||||
case State.SelectSecond:
|
||||
if (mi.Event != MouseInputEvent.Up)
|
||||
break;
|
||||
end = cell;
|
||||
selectionLayer.SetCopyRegion(start, end);
|
||||
state = State.Paste;
|
||||
break;
|
||||
case State.Paste:
|
||||
{
|
||||
if (mi.Event != MouseInputEvent.Down)
|
||||
break;
|
||||
var gridType = worldRenderer.World.Map.Grid.Type;
|
||||
var source = CellRegion.BoundingRegion(gridType, new[] { start, end });
|
||||
Copy(source, cell - end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var pastePosition = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
var action = new CopyPasteEditorAction(
|
||||
getCopyFilters(),
|
||||
resourceLayer,
|
||||
pastePosition,
|
||||
worldRenderer.World.Map,
|
||||
clipboard,
|
||||
editorActorLayer);
|
||||
|
||||
editorActionManager.Add(action);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Copy(CellRegion source, CVec offset)
|
||||
{
|
||||
var gridType = worldRenderer.World.Map.Grid.Type;
|
||||
var mapTiles = worldRenderer.World.Map.Tiles;
|
||||
var mapHeight = worldRenderer.World.Map.Height;
|
||||
var mapResources = worldRenderer.World.Map.Resources;
|
||||
|
||||
var dest = new CellRegion(gridType, source.TopLeft + offset, source.BottomRight + offset);
|
||||
|
||||
var previews = new Dictionary<string, ActorReference>();
|
||||
var tiles = new Dictionary<CPos, (TerrainTile, ResourceTile, byte)>();
|
||||
var copyFilters = getCopyFilters();
|
||||
|
||||
foreach (var cell in source)
|
||||
{
|
||||
if (!mapTiles.Contains(cell) || !mapTiles.Contains(cell + offset))
|
||||
continue;
|
||||
|
||||
tiles.Add(cell + offset, (mapTiles[cell], mapResources[cell], mapHeight[cell]));
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Actors))
|
||||
{
|
||||
foreach (var preview in editorLayer.PreviewsAt(cell))
|
||||
{
|
||||
if (previews.ContainsKey(preview.ID))
|
||||
continue;
|
||||
|
||||
var copy = preview.Export();
|
||||
var locationInit = copy.GetOrDefault<LocationInit>();
|
||||
if (locationInit != null)
|
||||
{
|
||||
copy.RemoveAll<LocationInit>();
|
||||
copy.Add(new LocationInit(locationInit.Value + offset));
|
||||
}
|
||||
|
||||
previews.Add(preview.ID, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var action = new CopyPasteEditorAction(copyFilters, worldRenderer.World.Map, tiles, previews, editorLayer, dest);
|
||||
editorActionManager.Add(action);
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
if (state == State.Paste)
|
||||
{
|
||||
selectionLayer.SetPasteRegion(cell + (start - end), cell);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == State.SelectFirst)
|
||||
start = end = cell;
|
||||
else if (state == State.SelectSecond)
|
||||
end = cell;
|
||||
|
||||
selectionLayer.SetCopyRegion(start, end);
|
||||
PastePreviewPosition = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
selectionLayer.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,33 +112,64 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
public string Text { get; }
|
||||
|
||||
readonly MapCopyFilters copyFilters;
|
||||
readonly Dictionary<CPos, (TerrainTile Tile, ResourceTile Resource, byte Height)> tiles;
|
||||
readonly Dictionary<string, ActorReference> previews;
|
||||
readonly EditorActorLayer editorLayer;
|
||||
readonly CellRegion dest;
|
||||
readonly CellLayer<TerrainTile> mapTiles;
|
||||
readonly CellLayer<byte> mapHeight;
|
||||
readonly CellLayer<ResourceTile> mapResources;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
readonly EditorActorLayer editorActorLayer;
|
||||
readonly EditorClipboard clipboard;
|
||||
readonly EditorClipboard undoClipboard;
|
||||
readonly CPos pastePosition;
|
||||
readonly Map map;
|
||||
|
||||
readonly Queue<UndoCopyPaste> undoCopyPastes = new();
|
||||
readonly Queue<EditorActorPreview> removedActors = new();
|
||||
readonly Queue<EditorActorPreview> addedActorPreviews = new();
|
||||
|
||||
public CopyPasteEditorAction(MapCopyFilters copyFilters, Map map,
|
||||
Dictionary<CPos, (TerrainTile Tile, ResourceTile Resource, byte Height)> tiles, Dictionary<string, ActorReference> previews,
|
||||
EditorActorLayer editorLayer, CellRegion dest)
|
||||
public CopyPasteEditorAction(
|
||||
MapCopyFilters copyFilters,
|
||||
IResourceLayer resourceLayer,
|
||||
CPos pastePosition,
|
||||
Map map,
|
||||
EditorClipboard clipboard,
|
||||
EditorActorLayer editorActorLayer)
|
||||
{
|
||||
this.copyFilters = copyFilters;
|
||||
this.tiles = tiles;
|
||||
this.previews = previews;
|
||||
this.editorLayer = editorLayer;
|
||||
this.dest = dest;
|
||||
this.resourceLayer = resourceLayer;
|
||||
this.clipboard = clipboard;
|
||||
this.pastePosition = pastePosition;
|
||||
this.editorActorLayer = editorActorLayer;
|
||||
this.map = map;
|
||||
|
||||
mapTiles = map.Tiles;
|
||||
mapHeight = map.Height;
|
||||
mapResources = map.Resources;
|
||||
undoClipboard = CopySelectionContents();
|
||||
|
||||
Text = TranslationProvider.GetString(CopiedTiles, Translation.Arguments("amount", tiles.Count));
|
||||
Text = TranslationProvider.GetString(CopiedTiles, Translation.Arguments("amount", clipboard.Tiles.Count));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO: This is pretty much repeated in MapEditorSelectionLogic.
|
||||
/// </summary>
|
||||
/// <returns>Clipboard containing map contents for this region.</returns>
|
||||
EditorClipboard CopySelectionContents()
|
||||
{
|
||||
var selectionSize = clipboard.CellRegion.BottomRight - clipboard.CellRegion.TopLeft;
|
||||
var source = new CellCoordsRegion(pastePosition, pastePosition + selectionSize);
|
||||
var selection = new CellRegion(map.Grid.Type, pastePosition, pastePosition + selectionSize);
|
||||
|
||||
var mapTiles = map.Tiles;
|
||||
var mapHeight = map.Height;
|
||||
var mapResources = map.Resources;
|
||||
|
||||
var previews = new Dictionary<string, EditorActorPreview>();
|
||||
var tiles = new Dictionary<CPos, ClipboardTile>();
|
||||
|
||||
foreach (var cell in source)
|
||||
{
|
||||
if (!mapTiles.Contains(cell))
|
||||
continue;
|
||||
|
||||
var resourceLayerContents = resourceLayer.GetResource(cell);
|
||||
tiles.Add(cell, new ClipboardTile(mapTiles[cell], mapResources[cell], resourceLayerContents, mapHeight[cell]));
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Actors))
|
||||
foreach (var preview in selection.SelectMany(editorActorLayer.PreviewsAt).Distinct())
|
||||
previews.TryAdd(preview.ID, preview);
|
||||
}
|
||||
|
||||
return new EditorClipboard(selection, previews, tiles);
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
@@ -218,72 +179,88 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
public void Do()
|
||||
{
|
||||
foreach (var kv in tiles)
|
||||
var sourcePos = clipboard.CellRegion.TopLeft;
|
||||
var pasteVec = new CVec(pastePosition.X - sourcePos.X, pastePosition.Y - sourcePos.Y);
|
||||
|
||||
foreach (var tileKeyValuePair in clipboard.Tiles)
|
||||
{
|
||||
undoCopyPastes.Enqueue(new UndoCopyPaste(kv.Key, mapTiles[kv.Key], mapResources[kv.Key], mapHeight[kv.Key]));
|
||||
var position = tileKeyValuePair.Key + pasteVec;
|
||||
if (!map.Contains(position))
|
||||
continue;
|
||||
|
||||
// Clear any existing resources.
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Resources))
|
||||
resourceLayer.ClearResources(position);
|
||||
|
||||
var tile = tileKeyValuePair.Value;
|
||||
var resourceLayerContents = tile.ResourceLayerContents;
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Terrain))
|
||||
mapTiles[kv.Key] = kv.Value.Tile;
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Resources))
|
||||
mapResources[kv.Key] = kv.Value.Resource;
|
||||
|
||||
mapHeight[kv.Key] = kv.Value.Height;
|
||||
}
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Actors))
|
||||
{
|
||||
var removeActors = dest.SelectMany(editorLayer.PreviewsAt).Distinct().ToList();
|
||||
foreach (var preview in removeActors)
|
||||
{
|
||||
removedActors.Enqueue(preview);
|
||||
editorLayer.Remove(preview);
|
||||
map.Tiles[position] = tile.TerrainTile;
|
||||
map.Height[position] = tile.Height;
|
||||
}
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Resources) && !string.IsNullOrWhiteSpace(resourceLayerContents.Type))
|
||||
resourceLayer.AddResource(resourceLayerContents.Type, position, resourceLayerContents.Density);
|
||||
}
|
||||
|
||||
foreach (var kv in previews)
|
||||
addedActorPreviews.Enqueue(editorLayer.Add(kv.Value));
|
||||
// Clear any existing actors in the paste cells.
|
||||
var selectionSize = clipboard.CellRegion.BottomRight - clipboard.CellRegion.TopLeft;
|
||||
var pasteRegion = new CellRegion(map.Grid.Type, pastePosition, pastePosition + selectionSize);
|
||||
foreach (var regionActor in pasteRegion.SelectMany(editorActorLayer.PreviewsAt).ToHashSet())
|
||||
editorActorLayer.Remove(regionActor);
|
||||
|
||||
// Now place actors.
|
||||
foreach (var actorKeyValuePair in clipboard.Actors)
|
||||
{
|
||||
var selection = clipboard.CellRegion;
|
||||
var copy = actorKeyValuePair.Value.Export();
|
||||
var locationInit = copy.GetOrDefault<LocationInit>();
|
||||
if (locationInit != null)
|
||||
{
|
||||
var actorPosition = locationInit.Value + new CVec(pastePosition.X - selection.TopLeft.X, pastePosition.Y - selection.TopLeft.Y);
|
||||
if (!map.Contains(actorPosition))
|
||||
continue;
|
||||
|
||||
copy.RemoveAll<LocationInit>();
|
||||
copy.Add(new LocationInit(actorPosition));
|
||||
}
|
||||
|
||||
editorActorLayer.Add(copy);
|
||||
}
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
while (undoCopyPastes.Count > 0)
|
||||
foreach (var tileKeyValuePair in undoClipboard.Tiles)
|
||||
{
|
||||
var undoCopyPaste = undoCopyPastes.Dequeue();
|
||||
var position = tileKeyValuePair.Key;
|
||||
var tile = tileKeyValuePair.Value;
|
||||
var resourceLayerContents = tile.ResourceLayerContents;
|
||||
|
||||
var cell = undoCopyPaste.Cell;
|
||||
// Clear any existing resources.
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Resources))
|
||||
resourceLayer.ClearResources(position);
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Terrain))
|
||||
mapTiles[cell] = undoCopyPaste.MapTile;
|
||||
{
|
||||
map.Tiles[position] = tile.TerrainTile;
|
||||
map.Height[position] = tile.Height;
|
||||
}
|
||||
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Resources))
|
||||
mapResources[cell] = undoCopyPaste.ResourceTile;
|
||||
|
||||
mapHeight[cell] = undoCopyPaste.Height;
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Resources) && !string.IsNullOrWhiteSpace(resourceLayerContents.Type))
|
||||
resourceLayer.AddResource(resourceLayerContents.Type, position, resourceLayerContents.Density);
|
||||
}
|
||||
|
||||
while (addedActorPreviews.Count > 0)
|
||||
editorLayer.Remove(addedActorPreviews.Dequeue());
|
||||
// Clear existing actors.
|
||||
foreach (var regionActor in undoClipboard.CellRegion.SelectMany(editorActorLayer.PreviewsAt).Distinct().ToList())
|
||||
editorActorLayer.Remove(regionActor);
|
||||
|
||||
// Place actors back again.
|
||||
if (copyFilters.HasFlag(MapCopyFilters.Actors))
|
||||
while (removedActors.Count > 0)
|
||||
editorLayer.Add(removedActors.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UndoCopyPaste
|
||||
{
|
||||
public CPos Cell { get; }
|
||||
public TerrainTile MapTile { get; }
|
||||
public ResourceTile ResourceTile { get; }
|
||||
public byte Height { get; }
|
||||
|
||||
public UndoCopyPaste(CPos cell, TerrainTile mapTile, ResourceTile resourceTile, byte height)
|
||||
{
|
||||
Cell = cell;
|
||||
MapTile = mapTile;
|
||||
ResourceTile = resourceTile;
|
||||
Height = height;
|
||||
foreach (var actor in undoClipboard.Actors.Values)
|
||||
editorActorLayer.Add(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
@@ -21,9 +22,17 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
void Tick();
|
||||
}
|
||||
|
||||
public class EditorSelection
|
||||
{
|
||||
public CellRegion Area;
|
||||
public EditorActorPreview Actor;
|
||||
}
|
||||
|
||||
public sealed class EditorDefaultBrush : IEditorBrush
|
||||
{
|
||||
public readonly ActorInfo Actor;
|
||||
const int MinMouseMoveBeforeDrag = 32;
|
||||
|
||||
public event Action SelectionChanged;
|
||||
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly World world;
|
||||
@@ -32,7 +41,13 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
readonly EditorActionManager editorActionManager;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
|
||||
public EditorActorPreview SelectedActor;
|
||||
public CellRegion CurrentDragBounds => selectionBounds ?? Selection.Area;
|
||||
|
||||
public EditorSelection Selection = new();
|
||||
EditorSelection previousSelection;
|
||||
CellRegion selectionBounds;
|
||||
int2? selectionStartLocation;
|
||||
CPos? selectionStartCell;
|
||||
int2 worldPixel;
|
||||
|
||||
public EditorDefaultBrush(EditorViewportControllerWidget editorWidget, WorldRenderer wr)
|
||||
@@ -58,13 +73,23 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return ((long)pixelDistance << 32) + worldZPosition;
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
if (Selection.Area != null || Selection.Actor != null)
|
||||
{
|
||||
previousSelection = Selection;
|
||||
Selection = new EditorSelection();
|
||||
editorActionManager.Add(new ChangeSelectionAction(this, Selection, previousSelection));
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HandleMouseInput(MouseInput mi)
|
||||
{
|
||||
// Exclusively uses mouse wheel and both mouse buttons, but nothing else
|
||||
// Mouse move events are important for tooltips, so we always allow these through
|
||||
if ((mi.Button != MouseButton.Left && mi.Button != MouseButton.Right
|
||||
&& mi.Event != MouseInputEvent.Move && mi.Event != MouseInputEvent.Scroll) ||
|
||||
mi.Event == MouseInputEvent.Down)
|
||||
// Exclusively uses mouse wheel and both mouse buttons, but nothing else.
|
||||
// Mouse move events are important for tooltips, so we always allow these through.
|
||||
if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right
|
||||
&& mi.Event != MouseInputEvent.Move && mi.Event != MouseInputEvent.Scroll)
|
||||
return false;
|
||||
|
||||
worldPixel = worldRenderer.Viewport.ViewToWorldPx(mi.Location);
|
||||
@@ -80,25 +105,114 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
else
|
||||
editorWidget.SetTooltip(null);
|
||||
|
||||
// Finished with mouse move events, so let them bubble up the widget tree
|
||||
// Selection box drag.
|
||||
if (selectionStartLocation != null &&
|
||||
(selectionBounds != null || (mi.Location - selectionStartLocation.Value).LengthSquared > MinMouseMoveBeforeDrag))
|
||||
{
|
||||
selectionStartCell ??= worldRenderer.Viewport.ViewToWorld(selectionStartLocation.Value);
|
||||
|
||||
var topLeft = new CPos(Math.Min(selectionStartCell.Value.X, cell.X), Math.Min(selectionStartCell.Value.Y, cell.Y));
|
||||
var bottomRight = new CPos(Math.Max(selectionStartCell.Value.X, cell.X), Math.Max(selectionStartCell.Value.Y, cell.Y));
|
||||
var width = bottomRight.X - topLeft.X;
|
||||
var height = bottomRight.Y - topLeft.Y;
|
||||
var gridType = worldRenderer.World.Map.Grid.Type;
|
||||
|
||||
// We've dragged enough to capture more than one cell, make a selection box.
|
||||
if (selectionBounds == null)
|
||||
{
|
||||
selectionBounds = new CellRegion(gridType, topLeft, bottomRight);
|
||||
|
||||
// Lose focus on any search boxes so we can always copy/paste.
|
||||
Ui.KeyboardFocusWidget = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We already have a drag box; resize it
|
||||
selectionBounds = new CellRegion(gridType, topLeft, bottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
// Finished with mouse move events, so let them bubble up the widget tree.
|
||||
if (mi.Event == MouseInputEvent.Move)
|
||||
return false;
|
||||
|
||||
if (mi.Button == MouseButton.Left)
|
||||
if (mi.Event == MouseInputEvent.Down && mi.Button == MouseButton.Left && selectionStartLocation == null)
|
||||
{
|
||||
editorWidget.SetTooltip(null);
|
||||
SelectedActor = underCursor;
|
||||
// Start area drag.
|
||||
selectionStartLocation = mi.Location;
|
||||
}
|
||||
|
||||
if (mi.Button == MouseButton.Right)
|
||||
if (mi.Event == MouseInputEvent.Up)
|
||||
{
|
||||
editorWidget.SetTooltip(null);
|
||||
if (mi.Button == MouseButton.Left)
|
||||
{
|
||||
editorWidget.SetTooltip(null);
|
||||
selectionStartLocation = null;
|
||||
selectionStartCell = null;
|
||||
|
||||
if (underCursor != null && underCursor != SelectedActor)
|
||||
editorActionManager.Add(new RemoveActorAction(editorLayer, underCursor));
|
||||
// If we've released a bounds drag.
|
||||
if (selectionBounds != null)
|
||||
{
|
||||
// Set this as the editor selection.
|
||||
previousSelection = Selection;
|
||||
Selection = new EditorSelection
|
||||
{
|
||||
Area = selectionBounds
|
||||
};
|
||||
|
||||
if (resourceUnderCursor != null)
|
||||
editorActionManager.Add(new RemoveResourceAction(resourceLayer, cell, resourceUnderCursor));
|
||||
editorActionManager.Add(new ChangeSelectionAction(this, Selection, previousSelection));
|
||||
selectionBounds = null;
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
else if (underCursor != null)
|
||||
{
|
||||
// We've clicked on an actor.
|
||||
if (Selection.Actor != underCursor)
|
||||
{
|
||||
previousSelection = Selection;
|
||||
Selection = new EditorSelection
|
||||
{
|
||||
Actor = underCursor,
|
||||
};
|
||||
|
||||
editorActionManager.Add(new ChangeSelectionAction(this, Selection, previousSelection));
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
else if (Selection.Area != null || Selection.Actor != null)
|
||||
{
|
||||
// Released left mouse without dragging or selecting an actor - deselect current.
|
||||
previousSelection = Selection;
|
||||
Selection = new EditorSelection();
|
||||
|
||||
editorActionManager.Add(new ChangeSelectionAction(this, Selection, previousSelection));
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
if (mi.Button == MouseButton.Right)
|
||||
{
|
||||
editorWidget.SetTooltip(null);
|
||||
|
||||
if (Selection.Area != null)
|
||||
{
|
||||
// Release right mouse button with a selection - clear selection.
|
||||
previousSelection = Selection;
|
||||
Selection = new EditorSelection();
|
||||
|
||||
editorActionManager.Add(new ChangeSelectionAction(this, Selection, previousSelection));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release right mouse button with no selection and over an actor - delete actor.
|
||||
if (underCursor != null && underCursor != Selection.Actor)
|
||||
editorActionManager.Add(new RemoveActorAction(editorLayer, underCursor));
|
||||
|
||||
// Or delete resource if found under cursor.
|
||||
if (resourceUnderCursor != null)
|
||||
editorActionManager.Add(new RemoveResourceAction(resourceLayer, cell, resourceUnderCursor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -108,6 +222,64 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
sealed class ChangeSelectionAction : IEditorAction
|
||||
{
|
||||
[TranslationReference("x", "y", "width", "height")]
|
||||
const string SelectedArea = "notification-selected-area";
|
||||
|
||||
[TranslationReference("id")]
|
||||
const string SelectedActor = "notification-selected-actor";
|
||||
|
||||
[TranslationReference]
|
||||
const string ClearedSelection = "notification-cleared-selection";
|
||||
|
||||
public string Text { get; }
|
||||
|
||||
readonly EditorSelection selection;
|
||||
readonly EditorSelection previousSelection;
|
||||
readonly EditorDefaultBrush defaultBrush;
|
||||
|
||||
public ChangeSelectionAction(
|
||||
EditorDefaultBrush defaultBrush,
|
||||
EditorSelection selection,
|
||||
EditorSelection previousSelection)
|
||||
{
|
||||
this.defaultBrush = defaultBrush;
|
||||
this.selection = selection;
|
||||
this.previousSelection = new EditorSelection
|
||||
{
|
||||
Actor = previousSelection.Actor,
|
||||
Area = previousSelection.Area
|
||||
};
|
||||
|
||||
if (selection.Area != null)
|
||||
Text = TranslationProvider.GetString(SelectedArea, Translation.Arguments(
|
||||
"x", selection.Area.TopLeft.X,
|
||||
"y", selection.Area.TopLeft.Y,
|
||||
"width", selection.Area.BottomRight.X - selection.Area.TopLeft.X,
|
||||
"height", selection.Area.BottomRight.Y - selection.Area.TopLeft.Y));
|
||||
else if (selection.Actor != null)
|
||||
Text = TranslationProvider.GetString(SelectedActor, Translation.Arguments("id", selection.Actor.ID));
|
||||
else
|
||||
Text = TranslationProvider.GetString(ClearedSelection);
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
Do();
|
||||
}
|
||||
|
||||
public void Do()
|
||||
{
|
||||
defaultBrush.Selection = selection;
|
||||
}
|
||||
|
||||
public void Undo()
|
||||
{
|
||||
defaultBrush.Selection = previousSelection;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RemoveActorAction : IEditorAction
|
||||
{
|
||||
[TranslationReference("name", "id")]
|
||||
|
||||
Reference in New Issue
Block a user