From bd590dfcf41d065f427d9577e611f315af5758f2 Mon Sep 17 00:00:00 2001 From: Gustas <37534529+PunkPun@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:40:11 +0300 Subject: [PATCH] Add a preview to editor marker brush --- .../EditorBrushes/EditorMarkerLayerBrush.cs | 123 +++++++++++------- .../Traits/World/MarkerLayerOverlay.cs | 7 +- 2 files changed, 78 insertions(+), 52 deletions(-) diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs index 09352f0d35..df59813646 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -26,8 +27,9 @@ namespace OpenRA.Mods.Common.Widgets readonly MarkerLayerOverlay markerLayerOverlay; readonly EditorViewportControllerWidget editorWidget; - PaintMarkerTileEditorAction action; + readonly List paintTiles = []; bool painting; + CPos cell; public EditorMarkerLayerBrush(EditorViewportControllerWidget editorWidget, int? id, WorldRenderer wr) { @@ -39,11 +41,11 @@ namespace OpenRA.Mods.Common.Widgets markerLayerOverlay = world.WorldActor.Trait(); Template = id; - action = new PaintMarkerTileEditorAction(Template, markerLayerOverlay); } public bool HandleMouseInput(MouseInput mi) { + // Exclusively uses left and right mouse buttons, but nothing else. if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right) return false; @@ -58,30 +60,71 @@ namespace OpenRA.Mods.Common.Widgets return false; } - if (mi.Button == MouseButton.Left && mi.Event != MouseInputEvent.Up) - { - action.Add(worldRenderer.Viewport.ViewToWorld(mi.Location)); - painting = true; - } - else if (painting && mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Up) - { - if (action.DidPaintTiles) - editorActionManager.Add(action); + if (mi.Button != MouseButton.Left) + return true; + + if (mi.Event == MouseInputEvent.Up) + { + UpdatePreview(); + if (paintTiles.Count != 0) + { + editorActionManager.Add(new PaintMarkerTileEditorAction(Template, paintTiles.ToImmutableArray(), markerLayerOverlay)); + paintTiles.Clear(); + UpdatePreview(true); + } - action = new PaintMarkerTileEditorAction(Template, markerLayerOverlay); painting = false; } + else + { + painting = true; + UpdatePreview(); + } return true; } - void IEditorBrush.TickRender(WorldRenderer wr, Actor self) { } + void UpdatePreview(bool forceRefresh = false) + { + var currentCell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); + if (!forceRefresh && cell == currentCell) + return; + + cell = currentCell; + + if (!painting) + { + foreach (var paintTile in paintTiles) + markerLayerOverlay.SetTile(paintTile.Cell, paintTile.Previous); + + paintTiles.Clear(); + } + + foreach (var cell in markerLayerOverlay.CalculateMirrorPositions(cell)) + { + if (paintTiles.Any(t => t.Cell == cell)) + continue; + + var existing = markerLayerOverlay.CellLayer[cell]; + if (existing == Template) + continue; + + paintTiles.Add(new PaintMarkerTile(cell, existing)); + markerLayerOverlay.SetTile(cell, Template); + } + } + + void IEditorBrush.TickRender(WorldRenderer wr, Actor self) { UpdatePreview(); } IEnumerable IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr) { yield break; } IEnumerable IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr) { yield break; } public void Tick() { } - public void Dispose() { } + public void Dispose() + { + foreach (var paintTile in paintTiles) + markerLayerOverlay.SetTile(paintTile.Cell, paintTile.Previous); + } } readonly struct PaintMarkerTile @@ -104,27 +147,28 @@ namespace OpenRA.Mods.Common.Widgets [FluentReference("count")] const string RemovedMarkerTiles = "notification-removed-marker-tiles"; - public string Text { get; private set; } + public string Text { get; } readonly int? type; readonly MarkerLayerOverlay markerLayerOverlay; - readonly List paintTiles = []; - - public bool DidPaintTiles => paintTiles.Count > 0; + readonly ImmutableArray paintTiles = []; public PaintMarkerTileEditorAction( int? type, + ImmutableArray paintTiles, MarkerLayerOverlay markerLayerOverlay) { - this.markerLayerOverlay = markerLayerOverlay; this.type = type; + this.paintTiles = paintTiles; + this.markerLayerOverlay = markerLayerOverlay; + + Text = type != null + ? FluentProvider.GetMessage(AddedMarkerTiles, "count", paintTiles.Length, "type", type) + : FluentProvider.GetMessage(RemovedMarkerTiles, "count", paintTiles.Length); } - public void Execute() - { - paintTiles.TrimExcess(); - } + public void Execute() { } public void Do() { @@ -137,23 +181,6 @@ namespace OpenRA.Mods.Common.Widgets foreach (var paintTile in paintTiles) markerLayerOverlay.SetTile(paintTile.Cell, paintTile.Previous); } - - public void Add(CPos target) - { - foreach (var cell in markerLayerOverlay.CalculateMirrorPositions(target)) - { - var existing = markerLayerOverlay.CellLayer[cell]; - if (existing == type) - continue; - - paintTiles.Add(new PaintMarkerTile(cell, existing)); - markerLayerOverlay.SetTile(cell, type); - } - - Text = type != null - ? FluentProvider.GetMessage(AddedMarkerTiles, "count", paintTiles.Count, "type", type) - : FluentProvider.GetMessage(RemovedMarkerTiles, "count", paintTiles.Count); - } } sealed class ClearSelectedMarkerTilesEditorAction : IEditorAction @@ -164,7 +191,7 @@ namespace OpenRA.Mods.Common.Widgets public string Text { get; } readonly MarkerLayerOverlay markerLayerOverlay; - readonly HashSet tiles; + readonly ImmutableArray tiles; readonly int tile; public ClearSelectedMarkerTilesEditorAction( @@ -174,9 +201,8 @@ namespace OpenRA.Mods.Common.Widgets this.tile = tile; this.markerLayerOverlay = markerLayerOverlay; - tiles = markerLayerOverlay.Tiles[tile].ToHashSet(); - - Text = FluentProvider.GetMessage(ClearedSelectedMarkerTiles, "count", tiles.Count, "type", tile); + tiles = markerLayerOverlay.Tiles[tile].ToImmutableArray(); + Text = FluentProvider.GetMessage(ClearedSelectedMarkerTiles, "count", tiles.Length, "type", tile); } public void Execute() @@ -191,7 +217,7 @@ namespace OpenRA.Mods.Common.Widgets public void Undo() { - markerLayerOverlay.SetSelected(tile, tiles); + markerLayerOverlay.SetSelected(tile, tiles.AsSpan()); } } @@ -203,15 +229,14 @@ namespace OpenRA.Mods.Common.Widgets public string Text { get; } readonly MarkerLayerOverlay markerLayerOverlay; - readonly Dictionary> tiles; + readonly ImmutableDictionary> tiles; public ClearAllMarkerTilesEditorAction( MarkerLayerOverlay markerLayerOverlay) { this.markerLayerOverlay = markerLayerOverlay; - tiles = new Dictionary>(markerLayerOverlay.Tiles); - - var allTilesCount = tiles.Values.Sum(x => x.Count); + tiles = markerLayerOverlay.Tiles.ToImmutableDictionary(t => t.Key, t => t.Value.ToImmutableArray()); + var allTilesCount = tiles.Values.Sum(x => x.Length); Text = FluentProvider.GetMessage(ClearedAllMarkerTiles, "count", allTilesCount); } diff --git a/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs b/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs index 77220296d6..60c8a3a30e 100644 --- a/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/MarkerLayerOverlay.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.Graphics; @@ -172,7 +173,7 @@ namespace OpenRA.Mods.Common.Traits NumSides = file.NumSides; AxisAngle = file.AxisAngle; - SetAll(file.Tiles.ToDictionary(d => d.Key, d => new HashSet(d.Value))); + SetAll(file.Tiles.ToImmutableDictionary(d => d.Key, d => d.Value.ToImmutableArray())); } catch (Exception e) { @@ -202,7 +203,7 @@ namespace OpenRA.Mods.Common.Traits Tiles.Clear(); } - public void SetAll(Dictionary> newTiles) + public void SetAll(IImmutableDictionary> newTiles) { ClearAll(); @@ -222,7 +223,7 @@ namespace OpenRA.Mods.Common.Traits } } - public void SetSelected(int tile, HashSet newTiles) + public void SetSelected(int tile, ReadOnlySpan newTiles) { var type = Tiles[tile]; foreach (var pos in type)