#nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; namespace ShrinkEventBus.Editor { public sealed class EventBusViewerWindow : EditorWindow { private sealed class BusRow { public string Key = string.Empty; public string FilterKey = string.Empty; public string Scheduler = string.Empty; public string Mode = string.Empty; public string Queue = string.Empty; public string Overflow = string.Empty; public int Subscribers; } private sealed class EventRow { public long Sequence; public DateTime TimestampUtc; public string Bus = string.Empty; public string EventType = string.Empty; public string ShortEventType = string.Empty; public string Scheduler = string.Empty; public string Mode = string.Empty; public int ThreadId; public double DurationMicroseconds; public bool IsAsync; public bool Accepted; public bool Handled; public bool Canceled; public ShrinkPostFailure Failure; public string Status => Failure != ShrinkPostFailure.None ? FormatFailure(Failure) : Canceled ? "已取消" : Handled ? "已处理" : "无处理器"; } private const int MaxEvents = 5000; private const int MaxPendingEvents = 20000; private static readonly Color SuccessColor = new(0.32f, 0.72f, 0.48f); private static readonly Color WarningColor = new(0.94f, 0.68f, 0.24f); private static readonly Color ErrorColor = new(0.95f, 0.36f, 0.34f); private static readonly Color MutedColor = new(0.58f, 0.61f, 0.66f); private readonly object _eventGate = new(); private readonly Queue _pendingEvents = new(); private readonly List _events = new(); private readonly List _filteredEvents = new(); private readonly List _buses = new(); private ListView? _busList; private ListView? _eventList; private Label? _eventEmptyState; private ToolbarSearchField? _search; private ToolbarToggle? _captureToggle; private ToolbarToggle? _problemOnlyToggle; private ToolbarToggle? _autoScrollToggle; private Label? _captureState; private Label? _status; private Label? _busDescription; private Label? _busCountMetric; private Label? _handlerCountMetric; private Label? _capturedMetric; private Label? _rateMetric; private Label? _latencyMetric; private Label? _problemMetric; private Label? _detailTitle; private Label? _detailTime; private Label? _detailBus; private Label? _detailExecution; private Label? _detailThread; private Label? _detailDuration; private Label? _detailResult; private string _selectedBus = string.Empty; private long _selectedEventSequence; private long _nextSequence; private long _droppedCaptureCount; private double _nextRefresh; private bool _captureRequested = true; private bool _diagnosticsSubscribed; [MenuItem("ShrinkSDK/事件总线/事件查看器")] private static void Open() { var window = GetWindow(); window.titleContent = new GUIContent("事件总线 2.0"); window.minSize = new Vector2(980f, 560f); window.Show(); } private void OnEnable() { SetDiagnosticsSubscription(_captureRequested); EditorApplication.update += Tick; } private void OnDisable() { SetDiagnosticsSubscription(false); EditorApplication.update -= Tick; } public void CreateGUI() { rootVisualElement.Clear(); rootVisualElement.style.flexDirection = FlexDirection.Column; rootVisualElement.style.backgroundColor = EditorGUIUtility.isProSkin ? new Color(0.105f, 0.112f, 0.125f) : new Color(0.88f, 0.89f, 0.91f); rootVisualElement.Add(BuildToolbar()); rootVisualElement.Add(BuildMetricsBand()); var mainSplit = new TwoPaneSplitView(0, 330f, TwoPaneSplitViewOrientation.Horizontal); mainSplit.style.flexGrow = 1f; mainSplit.Add(BuildBusPane()); mainSplit.Add(BuildEventWorkspace()); rootVisualElement.Add(mainSplit); _status = new Label(); _status.style.height = 24f; _status.style.paddingLeft = 10f; _status.style.paddingTop = 4f; _status.style.borderTopWidth = 1f; _status.style.borderTopColor = BorderColor(); _status.style.color = MutedColor; rootVisualElement.Add(_status); RefreshAll(); } private VisualElement BuildToolbar() { var toolbar = new Toolbar(); toolbar.style.height = 34f; var title = new Label("ShrinkEventBus"); title.style.unityFontStyleAndWeight = FontStyle.Bold; title.style.fontSize = 13f; title.style.marginLeft = 4f; title.style.marginRight = 8f; toolbar.Add(title); _captureState = new Label(); _captureState.style.minWidth = 48f; _captureState.style.height = 18f; _captureState.style.marginRight = 10f; _captureState.style.paddingLeft = 6f; _captureState.style.paddingRight = 6f; _captureState.style.unityTextAlign = TextAnchor.MiddleCenter; _captureState.style.unityFontStyleAndWeight = FontStyle.Bold; _captureState.style.fontSize = 9f; toolbar.Add(_captureState); _search = new ToolbarSearchField { tooltip = "按总线标识或事件类型筛选" }; _search.style.width = 260f; _search.RegisterValueChangedCallback(_ => ApplyFilter()); toolbar.Add(_search); _problemOnlyToggle = new ToolbarToggle { text = "仅问题" }; _problemOnlyToggle.tooltip = "只显示已取消、已拒绝或失败的发布"; _problemOnlyToggle.RegisterValueChangedCallback(_ => ApplyFilter()); toolbar.Add(_problemOnlyToggle); _autoScrollToggle = new ToolbarToggle { text = "跟随", value = true }; _autoScrollToggle.tooltip = "自动保持最新事件可见"; toolbar.Add(_autoScrollToggle); _captureToggle = new ToolbarToggle { text = "捕获", value = _captureRequested }; _captureToggle.tooltip = "启用详细分发采样"; _captureToggle.RegisterValueChangedCallback(evt => { _captureRequested = evt.newValue; SetDiagnosticsSubscription(evt.newValue); UpdateCaptureState(); }); toolbar.Add(_captureToggle); var spacer = new VisualElement(); spacer.style.flexGrow = 1f; toolbar.Add(spacer); toolbar.Add(new ToolbarButton(RefreshAll) { text = "刷新", tooltip = "刷新总线状态" }); toolbar.Add(new ToolbarButton(ExportCsv) { text = "导出", tooltip = "将当前可见事件导出为 CSV" }); toolbar.Add(new ToolbarButton(ClearEvents) { text = "清空", tooltip = "清空已捕获事件" }); UpdateCaptureState(); return toolbar; } private VisualElement BuildMetricsBand() { var band = new VisualElement(); band.style.height = 58f; band.style.flexDirection = FlexDirection.Row; band.style.borderBottomWidth = 1f; band.style.borderBottomColor = BorderColor(); band.style.backgroundColor = PanelColor(); _busCountMetric = AddMetric(band, "总线"); _handlerCountMetric = AddMetric(band, "处理器"); _capturedMetric = AddMetric(band, "已捕获"); _rateMetric = AddMetric(band, "事件 / 秒"); _latencyMetric = AddMetric(band, "平均分发耗时"); _problemMetric = AddMetric(band, "问题"); return band; } private static Label AddMetric(VisualElement parent, string caption) { var block = new VisualElement(); block.style.flexGrow = 1f; block.style.flexBasis = 0f; block.style.paddingLeft = 12f; block.style.paddingTop = 6f; block.style.borderRightWidth = 1f; block.style.borderRightColor = BorderColor(); var captionLabel = new Label(caption); captionLabel.style.fontSize = 9f; captionLabel.style.color = MutedColor; block.Add(captionLabel); var value = new Label("0"); value.style.fontSize = 17f; value.style.unityFontStyleAndWeight = FontStyle.Bold; value.style.marginTop = 1f; block.Add(value); parent.Add(block); return value; } private VisualElement BuildBusPane() { var pane = new VisualElement(); pane.style.flexGrow = 1f; pane.style.backgroundColor = PanelColor(); pane.Add(BuildBusHeader()); _busList = new ListView(_buses, 30f, MakeBusRow, BindBusRow) { selectionType = SelectionType.Single, showAlternatingRowBackgrounds = AlternatingRowBackground.ContentOnly, virtualizationMethod = CollectionVirtualizationMethod.FixedHeight }; _busList.style.flexGrow = 1f; _busList.selectionChanged += selection => { var selected = selection.OfType().FirstOrDefault(); if (selected == null) return; _selectedBus = selected.FilterKey; UpdateBusDescription(selected); ApplyFilter(); }; pane.Add(_busList); _busDescription = new Label(); _busDescription.style.minHeight = 48f; _busDescription.style.paddingLeft = 10f; _busDescription.style.paddingRight = 8f; _busDescription.style.paddingTop = 7f; _busDescription.style.paddingBottom = 6f; _busDescription.style.borderTopWidth = 1f; _busDescription.style.borderTopColor = BorderColor(); _busDescription.style.color = MutedColor; _busDescription.style.whiteSpace = WhiteSpace.Normal; pane.Add(_busDescription); return pane; } private VisualElement BuildEventWorkspace() { var split = new TwoPaneSplitView(1, 185f, TwoPaneSplitViewOrientation.Vertical); split.style.flexGrow = 1f; split.Add(BuildEventPane()); split.Add(BuildDetailPane()); return split; } private VisualElement BuildEventPane() { var pane = new VisualElement(); pane.style.flexGrow = 1f; pane.Add(BuildEventHeader()); _eventList = new ListView(_filteredEvents, 28f, MakeEventRow, BindEventRow) { selectionType = SelectionType.Single, showAlternatingRowBackgrounds = AlternatingRowBackground.ContentOnly, virtualizationMethod = CollectionVirtualizationMethod.FixedHeight }; _eventList.style.flexGrow = 1f; _eventList.selectionChanged += selection => { var selected = selection.OfType().FirstOrDefault(); if (selected == null) return; _selectedEventSequence = selected.Sequence; ShowEventDetails(selected); }; pane.Add(_eventList); _eventEmptyState = new Label("暂无事件"); _eventEmptyState.style.flexGrow = 1f; _eventEmptyState.style.unityTextAlign = TextAnchor.MiddleCenter; _eventEmptyState.style.color = MutedColor; pane.Add(_eventEmptyState); return pane; } private VisualElement BuildDetailPane() { var pane = new VisualElement(); pane.style.flexGrow = 1f; pane.style.backgroundColor = PanelColor(); pane.style.borderTopWidth = 1f; pane.style.borderTopColor = BorderColor(); _detailTitle = new Label("未选择事件"); _detailTitle.style.fontSize = 13f; _detailTitle.style.unityFontStyleAndWeight = FontStyle.Bold; _detailTitle.style.marginLeft = 12f; _detailTitle.style.marginTop = 9f; _detailTitle.style.marginBottom = 7f; pane.Add(_detailTitle); var grid = new VisualElement(); grid.style.flexDirection = FlexDirection.Row; grid.style.flexWrap = Wrap.Wrap; grid.style.paddingLeft = 8f; grid.style.paddingRight = 8f; _detailTime = AddDetailField(grid, "时间"); _detailBus = AddDetailField(grid, "总线"); _detailExecution = AddDetailField(grid, "执行方式"); _detailThread = AddDetailField(grid, "线程"); _detailDuration = AddDetailField(grid, "分发耗时"); _detailResult = AddDetailField(grid, "结果"); pane.Add(grid); return pane; } private static Label AddDetailField(VisualElement grid, string caption) { var field = new VisualElement(); field.style.width = Length.Percent(33.333f); field.style.minWidth = 190f; field.style.paddingLeft = 5f; field.style.paddingRight = 5f; field.style.paddingBottom = 8f; var captionLabel = new Label(caption); captionLabel.style.fontSize = 9f; captionLabel.style.color = MutedColor; field.Add(captionLabel); var value = new Label("-"); value.style.marginTop = 2f; value.style.overflow = Overflow.Hidden; value.style.whiteSpace = WhiteSpace.Normal; field.Add(value); grid.Add(field); return value; } private static VisualElement BuildBusHeader() { var header = BuildHeaderContainer("总线"); var columns = BuildColumnRow(); columns.Add(MakeHeaderCell("标识", 1f)); columns.Add(MakeHeaderCell("调度器", 0f, 92f)); columns.Add(MakeHeaderCell("处理器", 0f, 58f, TextAnchor.MiddleRight)); header.Add(columns); return header; } private static VisualElement BuildEventHeader() { var header = BuildHeaderContainer("事件流"); var columns = BuildColumnRow(); columns.Add(MakeHeaderCell("时间", 0f, 92f)); columns.Add(MakeHeaderCell("总线", 0f, 132f)); columns.Add(MakeHeaderCell("事件", 1f)); columns.Add(MakeHeaderCell("方式", 0f, 54f)); columns.Add(MakeHeaderCell("线程", 0f, 56f, TextAnchor.MiddleRight)); columns.Add(MakeHeaderCell("耗时", 0f, 82f, TextAnchor.MiddleRight)); columns.Add(MakeHeaderCell("结果", 0f, 90f)); header.Add(columns); return header; } private static VisualElement BuildHeaderContainer(string title) { var header = new VisualElement(); header.style.height = 52f; header.style.borderBottomWidth = 1f; header.style.borderBottomColor = BorderColor(); header.style.backgroundColor = HeaderColor(); var titleLabel = new Label(title); titleLabel.style.height = 27f; titleLabel.style.paddingLeft = 10f; titleLabel.style.paddingTop = 7f; titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; titleLabel.style.fontSize = 10f; header.Add(titleLabel); return header; } private static VisualElement BuildColumnRow() { var row = new VisualElement(); row.style.height = 24f; row.style.flexDirection = FlexDirection.Row; row.style.alignItems = Align.Center; return row; } private static Label MakeHeaderCell(string text, float grow, float width = 0f, TextAnchor alignment = TextAnchor.MiddleLeft) { var label = MakeCell(string.Empty, grow, width, alignment); label.text = text; label.style.fontSize = 9f; label.style.color = MutedColor; return label; } private static VisualElement MakeBusRow() { var row = new VisualElement { name = "bus-row" }; row.style.flexDirection = FlexDirection.Row; row.style.alignItems = Align.Center; row.Add(MakeCell("key", 1f)); row.Add(MakeCell("scheduler", 0f, 92f)); row.Add(MakeCell("handlers", 0f, 58f, TextAnchor.MiddleRight)); return row; } private void BindBusRow(VisualElement element, int index) { if (index < 0 || index >= _buses.Count) return; var item = _buses[index]; element.Q