using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Ichni.Editor.Commands; using Ichni.RhythmGame; using Ichni.RhythmGame.Beatmap; using UnityEngine; using UnityEngine.InputSystem; namespace Ichni.Editor { public class OperationManager { public List currentSelectedElements { get; private set; } public TempOutlineModule TempOutlineModule = new TempOutlineModule(); public CopyPasteDeleteModule CopyPasteDeleteModule; public FindingModule FindingModule; public OperationManager() { currentSelectedElements = new List(); CopyPasteDeleteModule = new CopyPasteDeleteModule(); FindingModule = new FindingModule(); } public void AddSelectElement(GameElement gameElement) { if (gameElement == null || gameElement.connectedTab == null) return; if (currentSelectedElements.Contains(gameElement)) return; currentSelectedElements.Add(gameElement); gameElement.connectedTab.isSelected = true; if (gameElement.connectedTab.BgImage != null) { gameElement.connectedTab.BgImage.color = new Color(0f, 0f, 0f, 0.8f); } } public void AddSelectRangeToElement(GameElement targetElement) { if (targetElement == null) return; GameElement parentElement = targetElement.parentElement; List siblingElements = parentElement?.childElementList; if (siblingElements == null || siblingElements.Count == 0) { AddSelectElement(targetElement); return; } int targetIndex = siblingElements.IndexOf(targetElement); if (targetIndex < 0) { AddSelectElement(targetElement); return; } GameElement rangeStartElement = currentSelectedElements .Where(element => element != null && element.parentElement == parentElement) .Select(element => new { Element = element, Index = siblingElements.IndexOf(element) }) .Where(entry => entry.Index >= 0) .OrderByDescending(entry => entry.Index) .Select(entry => entry.Element) .FirstOrDefault(); if (rangeStartElement == null) { if (currentSelectedElements.Count > 0) { LogWindow.Log("Shift range select requires sibling elements.", Color.red); } AddSelectElement(targetElement); return; } int startIndex = siblingElements.IndexOf(rangeStartElement); int minIndex = Mathf.Min(startIndex, targetIndex); int maxIndex = Mathf.Max(startIndex, targetIndex); for (int i = minIndex; i <= maxIndex; i++) { AddSelectElement(siblingElements[i]); } } public void RemoveSelectElement(GameElement gameElement) { if (gameElement == null) return; currentSelectedElements.Remove(gameElement); if (gameElement.connectedTab == null) return; gameElement.connectedTab.isSelected = false; if (gameElement.connectedTab.BgImage != null) { gameElement.connectedTab.BgImage.color = new Color(0.5f, 0.5f, 0.5f, 0); } } public void ClearSelectedElements() { currentSelectedElements.RemoveAll(gameElement => gameElement == null); currentSelectedElements.ForEach(gameElement => { if (gameElement == null || gameElement.connectedTab == null) return; gameElement.connectedTab.isSelected = false; if (gameElement.connectedTab.BgImage != null) { gameElement.connectedTab.BgImage.color = new Color(0.5f, 0.5f, 0.5f, 0); } }); currentSelectedElements.Clear(); } } public class TempOutlineModule { public List outlinedElements; public Material outlineMaterial => EditorManager.instance.basePrefabs.outlineShaderMaterial; public TempOutlineModule() { outlinedElements = new List(); } public void AddOutline(GameElement gameElement) { outlinedElements.Add(gameElement); } public void RemoveOutline(GameElement gameElement) { outlinedElements.Remove(gameElement); } public void ClearOutline() { outlinedElements.Clear(); } } public class FindingModule { public GameElement FindGameElementByName(string elementName) { foreach (GameElement element in EditorManager.instance.beatmapContainer.gameElementList) { if (element.elementName == elementName) { return element; } } LogWindow.Log("Element not found: " + elementName, Color.red); return null; } } public class CopyPasteDeleteModule { public GameElement copiedElement; public List pastedElementList; private ElementClipboardSnapshot clipboardSnapshot; public void CopyElement(GameElement gameElement) { CopyElements(new[] { gameElement }); } public void CopyElements(IEnumerable gameElements) { if (!ElementClipboardSnapshot.TryCaptureSiblingRoots( gameElements, out ElementClipboardSnapshot snapshot, out List orderedRoots, out string errorMessage)) { LogWindow.Log(errorMessage, Color.red); return; } clipboardSnapshot = snapshot; copiedElement = orderedRoots.FirstOrDefault(); string elementText = orderedRoots.Count == 1 ? orderedRoots[0].elementName : orderedRoots.Count + " sibling elements"; LogWindow.Log("Copied element: " + elementText); } public void CutElement(GameElement gameElement) { CutElements(new[] { gameElement }); } public void CutElements(IEnumerable gameElements) { if (!ElementClipboardSnapshot.TryCaptureSiblingRoots( gameElements, out ElementClipboardSnapshot snapshot, out List orderedRoots, out string errorMessage)) { LogWindow.Log(errorMessage, Color.red); return; } clipboardSnapshot = snapshot; copiedElement = orderedRoots.FirstOrDefault(); ExecuteDeleteElements(orderedRoots); LogWindow.Log("Cut element: " + (orderedRoots.Count == 1 ? orderedRoots[0].elementName : orderedRoots.Count + " sibling elements")); } public void PasteElement(GameElement parentElement) { if (parentElement == null) { LogWindow.Log("No parent element selected to paste.", Color.red); return; } if (clipboardSnapshot == null || clipboardSnapshot.RootCount == 0) { LogWindow.Log("No element copied."); return; } CommandManager.ExecuteCommand(new PasteElementCommand(this, clipboardSnapshot, parentElement)); } public GameElement PasteElementRaw(GameElement sourceElement, GameElement parentElement) { ElementClipboardSnapshot snapshot = sourceElement == copiedElement && clipboardSnapshot != null ? clipboardSnapshot : ElementClipboardSnapshot.CaptureSingle(sourceElement); return PasteElementsRaw(snapshot, parentElement).FirstOrDefault(); } public List PasteElementsRaw(ElementClipboardSnapshot snapshot, GameElement parentElement) { pastedElementList = new List(); if (snapshot == null || parentElement == null) return new List(); ElementPasteResult pasteResult = snapshot.PasteTo(parentElement); pastedElementList = pasteResult.AllElements; string pastedText = pasteResult.RootElements.Count == 1 ? pasteResult.RootElements[0].elementName : pasteResult.RootElements.Count + " elements"; LogWindow.Log("Pasted element: " + pastedText + " to " + parentElement.elementName); return pasteResult.RootElements; } public void DeleteElement(GameElement gameElement) { DeleteElements(new[] { gameElement }); } public void DeleteElements(IEnumerable gameElements) { List roots = ElementClipboardSnapshot.GetTopLevelElements(gameElements); if (roots.Count == 0) { LogWindow.Log("No element selected to delete.", Color.red); return; } ExecuteDeleteElements(roots); } private void ExecuteDeleteElements(IEnumerable gameElements) { List roots = ElementClipboardSnapshot.GetTopLevelElements(gameElements); if (roots.Count == 0) return; if (roots.Count == 1) { CommandManager.ExecuteCommand(new DeleteElementCommand(roots[0])); return; } CommandGroup commandGroup = new CommandGroup(); foreach (GameElement root in roots) { commandGroup.Add(new DeleteElementCommand(root)); } if (commandGroup.Count > 0) { CommandManager.ExecuteCommand(commandGroup); } } public void DeleteElementRaw(GameElement gameElement, bool writeLog = true) { if (gameElement == null) return; var deletedElements = gameElement.GetAllGameElementsFromThis(); var deletedGuids = deletedElements.Select(element => element.elementGuid).ToList(); var deletedTabs = deletedElements .Select(element => element.connectedTab) .Where(tab => tab != null) .ToList(); if (writeLog) LogWindow.Log("Deleted element: " + gameElement.elementName + " and all its children."); if (gameElement.parentElement != null) { gameElement.parentElement.childElementList.Remove(gameElement); //从父物体的子物体列表中移除,避免报null if (gameElement.parentElement.connectedTab != null) { gameElement.parentElement.connectedTab.childTabList.Remove(gameElement.connectedTab); gameElement.parentElement.connectedTab.SetStatus(); } } deletedElements.ForEach(element => EditorManager.instance.operationManager.RemoveSelectElement(element)); gameElement.Delete(); deletedGuids.ForEach(guid => GameElement_BM.identifier.Remove(guid)); deletedTabs.ForEach(tab => EditorManager.instance.uiManager.hierarchy.tabList.Remove(tab)); EditorManager.instance.uiManager.inspector.ClearInspector(); } } public interface IPastedReferenceResolver { void ResolvePastedReferences(IReadOnlyDictionary pastedElementBySourceGuid); } }