Lower inventory placement previews in Unity
This commit is contained in:
parent
c6b7ecebc5
commit
3af959bf4f
3 changed files with 129 additions and 5 deletions
|
|
@ -17,6 +17,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
private Vector2 _inventoryDragStart;
|
||||
private bool _inventoryPointerMoved;
|
||||
private bool _suppressInventoryClick;
|
||||
private readonly List<VisualElement> _inventoryPreviewCells = new();
|
||||
|
||||
public EveUiToolkitSurfaceLowerer(EveUiToolkitSurfaceOptions? options = null)
|
||||
{
|
||||
|
|
@ -273,6 +274,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
return;
|
||||
if (TryEmitInventoryDrop(document, _inventoryDragSource, component, grid, evt.position, commandSink))
|
||||
_inventoryDragSource = null;
|
||||
ClearInventoryPreview();
|
||||
});
|
||||
return grid;
|
||||
}
|
||||
|
|
@ -291,6 +293,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
_inventoryDragSource = component;
|
||||
_inventoryDragStart = evt.position;
|
||||
_inventoryPointerMoved = false;
|
||||
ClearInventoryPreview();
|
||||
item.CapturePointer(evt.pointerId);
|
||||
evt.StopPropagation();
|
||||
});
|
||||
|
|
@ -300,7 +303,15 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
return;
|
||||
var pointerPosition = new Vector2(evt.position.x, evt.position.y);
|
||||
if ((pointerPosition - _inventoryDragStart).sqrMagnitude > 16f)
|
||||
{
|
||||
_inventoryPointerMoved = true;
|
||||
var picked = item.panel?.Pick(evt.position);
|
||||
var targetElement = InventoryGridAncestor(picked);
|
||||
if (targetElement?.userData is EveSurfaceComponent target)
|
||||
ShowInventoryPreview(component, target, targetElement, evt.position);
|
||||
else
|
||||
ClearInventoryPreview();
|
||||
}
|
||||
});
|
||||
item.RegisterCallback<PointerUpEvent>(evt =>
|
||||
{
|
||||
|
|
@ -315,6 +326,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
TryEmitInventoryDrop(document, component, target, targetElement, evt.position, commandSink);
|
||||
_inventoryDragSource = null;
|
||||
_suppressInventoryClick = true;
|
||||
ClearInventoryPreview();
|
||||
}
|
||||
evt.StopPropagation();
|
||||
});
|
||||
|
|
@ -399,6 +411,9 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
Math.Max(0f, ParseNumber(target.GetProp("cellGap"), 4f)));
|
||||
var x = Math.Max(0, (int)Math.Floor(local.x / pitch));
|
||||
var y = Math.Max(0, (int)Math.Floor(local.y / pitch));
|
||||
if (!EveInventoryInteraction.TryCreatePlacementPreview(source, target, x, y, out var preview) ||
|
||||
preview == null || !preview.IsValid)
|
||||
return false;
|
||||
if (!EveInventoryInteraction.TryCreateDropRequest(
|
||||
document, source, target, x, y, "unity-uitoolkit", out var request) || request == null)
|
||||
return false;
|
||||
|
|
@ -406,6 +421,50 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
return true;
|
||||
}
|
||||
|
||||
private void ShowInventoryPreview(
|
||||
EveSurfaceComponent source,
|
||||
EveSurfaceComponent target,
|
||||
VisualElement targetElement,
|
||||
Vector2 panelPosition)
|
||||
{
|
||||
ClearInventoryPreview();
|
||||
var local = targetElement.WorldToLocal(panelPosition);
|
||||
var cellSize = Math.Max(1f, ParseNumber(target.GetProp("cellSize"), 72f));
|
||||
var gap = Math.Max(0f, ParseNumber(target.GetProp("cellGap"), 4f));
|
||||
var pitch = cellSize + gap;
|
||||
var x = Math.Max(0, (int)Math.Floor(local.x / Math.Max(1f, pitch)));
|
||||
var y = Math.Max(0, (int)Math.Floor(local.y / Math.Max(1f, pitch)));
|
||||
if (!EveInventoryInteraction.TryCreatePlacementPreview(source, target, x, y, out var preview) ||
|
||||
preview == null)
|
||||
return;
|
||||
|
||||
foreach (var cell in preview.Cells)
|
||||
{
|
||||
var element = new VisualElement { pickingMode = PickingMode.Ignore };
|
||||
element.AddToClassList("eve-inventory-placement-preview");
|
||||
element.AddToClassList(preview.IsValid
|
||||
? "eve-inventory-placement-valid"
|
||||
: "eve-inventory-placement-invalid");
|
||||
element.style.position = Position.Absolute;
|
||||
element.style.left = cell.X * pitch;
|
||||
element.style.top = cell.Y * pitch;
|
||||
element.style.width = cellSize;
|
||||
element.style.height = cellSize;
|
||||
element.style.backgroundColor = preview.IsValid
|
||||
? new Color(0.2f, 0.9f, 0.65f, 0.34f)
|
||||
: new Color(1f, 0.25f, 0.2f, 0.38f);
|
||||
targetElement.Add(element);
|
||||
_inventoryPreviewCells.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearInventoryPreview()
|
||||
{
|
||||
foreach (var cell in _inventoryPreviewCells)
|
||||
cell.RemoveFromHierarchy();
|
||||
_inventoryPreviewCells.Clear();
|
||||
}
|
||||
|
||||
private static int ParseInt(string value, int fallback) =>
|
||||
int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed) ? parsed : fallback;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameCult.Eve.Surface;
|
||||
using GameCult.Mesh;
|
||||
using NUnit.Framework;
|
||||
|
|
@ -244,18 +245,64 @@ namespace GameCult.Eve.UnityUIToolkit.Tests
|
|||
["targetKind"] = "equipment",
|
||||
["targetEntityKey"] = "zone.0.entity.1",
|
||||
["targetIndex"] = "-1",
|
||||
["dropCommand.cargo"] = "aetheria.daemon.commands.EquipItem"
|
||||
["dropCommand.cargo"] = "aetheria.daemon.commands.EquipItem",
|
||||
["payload.shipId"] = "hangar.ship.1",
|
||||
["payload.expectedHangarRevision"] = "42"
|
||||
});
|
||||
|
||||
Assert.That(EveInventoryInteraction.TryCreateDropRequest(
|
||||
Document(target), source, target, 7, 9, "unity-test", out var request), Is.True);
|
||||
Assert.That(request, Is.Not.Null);
|
||||
Assert.That(request!.Command, Is.EqualTo("aetheria.daemon.commands.EquipItem"));
|
||||
Assert.That(request.CommandId, Is.Not.Empty);
|
||||
Assert.That(request.Payload.GetString("originEntityKey"), Is.EqualTo("zone.0.entity.1"));
|
||||
Assert.That(request.Payload.GetString("originCargoIndex"), Is.EqualTo("2"));
|
||||
Assert.That(request.Payload.GetString("destinationX"), Is.EqualTo("7"));
|
||||
Assert.That(request.Payload.GetString("destinationY"), Is.EqualTo("9"));
|
||||
Assert.That(request.Payload.GetString("hasDestinationPosition"), Is.EqualTo("true"));
|
||||
Assert.That(request.Payload.GetString("shipId"), Is.EqualTo("hangar.ship.1"));
|
||||
Assert.That(request.Payload.GetString("expectedHangarRevision"), Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InventoryPlacementPreviewUsesIrregularShapeAndCurrentOccupancy()
|
||||
{
|
||||
var moving = Component("moving", EveInventoryInteraction.ItemKind, new Dictionary<string, string>
|
||||
{
|
||||
["shapeCells"] = "0,0;1,0;0,1"
|
||||
});
|
||||
var installed = Component("installed", EveInventoryInteraction.ItemKind, new Dictionary<string, string>
|
||||
{
|
||||
["x"] = "2",
|
||||
["y"] = "1",
|
||||
["shapeCells"] = "0,0"
|
||||
});
|
||||
var grid = new EveSurfaceComponent(
|
||||
"grid",
|
||||
EveInventoryInteraction.GridKind,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["columns"] = "4",
|
||||
["rows"] = "3",
|
||||
["validCells"] = "0,0;1,0;2,0;3,0;0,1;1,1;2,1;3,1;0,2;1,2;2,2;3,2"
|
||||
},
|
||||
new[] { installed });
|
||||
|
||||
Assert.That(EveInventoryInteraction.TryCreatePlacementPreview(
|
||||
moving, grid, 0, 0, out var valid), Is.True);
|
||||
Assert.That(valid!.IsValid, Is.True);
|
||||
Assert.That(valid.Cells.Select(cell => (cell.X, cell.Y)),
|
||||
Is.EquivalentTo(new[] { (0, 0), (1, 0), (0, 1) }));
|
||||
|
||||
Assert.That(EveInventoryInteraction.TryCreatePlacementPreview(
|
||||
moving, grid, 1, 1, out var occupied), Is.True);
|
||||
Assert.That(occupied!.IsValid, Is.False);
|
||||
Assert.That(occupied.Reason, Is.EqualTo("occupied"));
|
||||
|
||||
Assert.That(EveInventoryInteraction.TryCreatePlacementPreview(
|
||||
moving, grid, 3, 2, out var outside), Is.True);
|
||||
Assert.That(outside!.IsValid, Is.False);
|
||||
Assert.That(outside.Reason, Is.EqualTo("outside-grid"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
|
|
@ -14,9 +14,21 @@ foreach ($required in @($UnityExe, $projectPath, $cultLibBuilder)) {
|
|||
if (-not (Test-Path -LiteralPath $required)) { throw "Required EveUnity UI Toolkit test path not found: $required" }
|
||||
}
|
||||
|
||||
powershell -ExecutionPolicy Bypass -File $cultLibBuilder
|
||||
& $cultLibBuilder
|
||||
if ($LASTEXITCODE -ne 0) { throw "CultLib Unity package build failed with exit code $LASTEXITCODE" }
|
||||
|
||||
$builtCultLibPackage = Join-Path $CultLibRoot "artifacts\unity\org.gamecult.cultlib"
|
||||
$stagedDependenciesRoot = Join-Path $repoRoot "artifacts\test-dependencies"
|
||||
$stagedCultLibPackage = Join-Path $stagedDependenciesRoot "org.gamecult.cultlib"
|
||||
if (-not (Test-Path -LiteralPath $builtCultLibPackage)) {
|
||||
throw "CultLib Unity package build did not produce: $builtCultLibPackage"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $stagedDependenciesRoot | Out-Null
|
||||
if (Test-Path -LiteralPath $stagedCultLibPackage) {
|
||||
[IO.Directory]::Delete($stagedCultLibPackage, $true)
|
||||
}
|
||||
Copy-Item -LiteralPath $builtCultLibPackage -Destination $stagedCultLibPackage -Recurse
|
||||
|
||||
$runRoot = Join-Path $output (Get-Date -Format "yyyyMMddTHHmmss")
|
||||
New-Item -ItemType Directory -Force -Path $runRoot | Out-Null
|
||||
$resultsPath = Join-Path $runRoot "unity-editmode-results.xml"
|
||||
|
|
@ -27,10 +39,16 @@ $arguments = @(
|
|||
"-assemblyNames", "GameCult.Eve.UnityUIToolkit.Tests",
|
||||
"-testResults", $resultsPath, "-logFile", $logPath
|
||||
)
|
||||
$process = Start-Process -FilePath $UnityExe -ArgumentList $arguments -Wait -PassThru -WindowStyle Hidden
|
||||
if ($process.ExitCode -ne 0) {
|
||||
$process = Start-Process -FilePath $UnityExe -ArgumentList $arguments -PassThru -WindowStyle Hidden
|
||||
if (-not $process.WaitForExit([int][TimeSpan]::FromMinutes(10).TotalMilliseconds)) {
|
||||
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
||||
if (Test-Path -LiteralPath $logPath) { Get-Content -LiteralPath $logPath -Tail 120 }
|
||||
throw "EveUnity UI Toolkit tests failed with exit code $($process.ExitCode)"
|
||||
throw "EveUnity UI Toolkit tests timed out after 10 minutes (Unity PID $($process.Id))."
|
||||
}
|
||||
$unityExitCode = $process.ExitCode
|
||||
if ($unityExitCode -ne 0) {
|
||||
if (Test-Path -LiteralPath $logPath) { Get-Content -LiteralPath $logPath -Tail 120 }
|
||||
throw "EveUnity UI Toolkit tests failed with exit code $unityExitCode"
|
||||
}
|
||||
|
||||
[xml]$results = Get-Content -LiteralPath $resultsPath -Raw
|
||||
|
|
|
|||
Reference in a new issue