diff --git a/Editor.meta b/Editor.meta new file mode 100644 index 0000000..8976890 --- /dev/null +++ b/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7d89f89053ce0384c9f7b48a5b491bca +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/EncounterBrowserWindow.cs b/Editor/EncounterBrowserWindow.cs new file mode 100644 index 0000000..9deb9b9 --- /dev/null +++ b/Editor/EncounterBrowserWindow.cs @@ -0,0 +1,341 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Jovian.EncounterSystem; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Jovian.EncounterSystem.Editor { + /// + /// Designer-facing browser for every authored across all + /// assets in the project. Virtualised ListView on the left, + /// property-field detail pane on the right. Search by id/name/description; filter by kind. + /// + public class EncounterBrowserWindow : EditorWindow { + private const string AllKinds = "All"; + + private class Record { + public EncounterTable table; + public int index; + public IEncounter encounter; + } + + private readonly List allRecords = new(); + private List filteredRecords = new(); + private string searchText = string.Empty; + private string kindFilter = AllKinds; + + private readonly Dictionary> issuesByEncounter = new(); + + private ListView listView; + private VisualElement detailPane; + private ToolbarMenu kindDropdown; + + [MenuItem("Jovian/Encounters/Encounter Browser")] + public static void Open() { + var window = GetWindow("Encounters"); + window.minSize = new Vector2(640, 360); + } + + private void CreateGUI() { + BuildToolbar(); + BuildSplit(); + Refresh(); + } + + private void BuildToolbar() { + var toolbar = new Toolbar(); + + var search = new ToolbarSearchField(); + search.style.flexGrow = 1f; + search.RegisterValueChangedCallback(evt => { + searchText = evt.newValue ?? string.Empty; + ApplyFilter(); + }); + toolbar.Add(search); + + kindDropdown = new ToolbarMenu { text = $"Kind: {AllKinds}" }; + foreach(var choice in GetKindChoices()) { + var captured = choice; + kindDropdown.menu.AppendAction(captured, _ => { + kindFilter = captured; + kindDropdown.text = $"Kind: {captured}"; + ApplyFilter(); + }); + } + toolbar.Add(kindDropdown); + + var refreshButton = new ToolbarButton(Refresh) { text = "Refresh" }; + toolbar.Add(refreshButton); + + rootVisualElement.Add(toolbar); + } + + private void BuildSplit() { + var split = new TwoPaneSplitView(0, 280, TwoPaneSplitViewOrientation.Horizontal); + split.style.flexGrow = 1f; + rootVisualElement.Add(split); + + listView = new ListView { + makeItem = MakeRow, + bindItem = BindRow, + fixedItemHeight = 22, + selectionType = SelectionType.Single + }; + listView.selectionChanged += OnSelectionChanged; + listView.style.flexGrow = 1f; + split.Add(listView); + + detailPane = new ScrollView(ScrollViewMode.Vertical) { + style = { paddingLeft = 8, paddingTop = 8, paddingRight = 8, flexGrow = 1f } + }; + ShowEmptyDetail(); + split.Add(detailPane); + } + + private static VisualElement MakeRow() { + var row = new VisualElement { + style = { + flexDirection = FlexDirection.Row, + alignItems = Align.Center, + paddingLeft = 6, + paddingRight = 6, + height = 22 + } + }; + + var badge = new VisualElement { + name = "issue-badge", + style = { + width = 8, + height = 8, + marginRight = 6, + borderTopLeftRadius = 4, + borderTopRightRadius = 4, + borderBottomLeftRadius = 4, + borderBottomRightRadius = 4, + visibility = Visibility.Hidden + } + }; + row.Add(badge); + + var label = new Label { + name = "row-label", + style = { + flexGrow = 1f, + unityTextAlign = TextAnchor.MiddleLeft + } + }; + row.Add(label); + + return row; + } + + private void BindRow(VisualElement element, int index) { + var record = filteredRecords[index]; + var label = element.Q