using AlicizaX.Timer.Runtime; using UnityEngine; using UnityEngine.UIElements; namespace AlicizaX.Debugger.Runtime { public sealed partial class DebuggerComponent { private sealed class TimerInformationWindow : ScrollableDebuggerWindowBase { private const int MAX_DISPLAY_COUNT = 50; private struct RowView { public VisualElement Root; public Label Title; public Label Value; } private ITimerServiceDebugView m_TimerDebugView; private TimerDebugInfo[] m_TimerInfos; private Label m_SectionTitleLabel; private Label m_ActiveCountLabel; private Label m_PoolCapacityLabel; private Label m_PeakCountLabel; private Label m_FreeCountLabel; private Label m_UsageLabel; private Label m_WarningLabel; private RowView m_EmptyRow; private readonly RowView[] m_TimerRows = new RowView[MAX_DISPLAY_COUNT]; public override void Initialize(params object[] args) { m_TimerDebugView = AppServices.Require() as ITimerServiceDebugView; } protected override void BuildWindow(VisualElement root) { if (m_TimerDebugView == null) { return; } root.Add(CreateActionButton("Refresh", RefreshContent, DebuggerTheme.ButtonSurfaceActive, DebuggerTheme.PrimaryText)); VisualElement overview = CreateSection("Timer Pool Overview", out VisualElement overviewCard); m_ActiveCountLabel = AddTextRow(overviewCard, "Active Timer Count").Value; m_PoolCapacityLabel = AddTextRow(overviewCard, "Pool Capacity").Value; m_PeakCountLabel = AddTextRow(overviewCard, "Peak Active Count").Value; m_FreeCountLabel = AddTextRow(overviewCard, "Free Count").Value; m_UsageLabel = AddTextRow(overviewCard, "Pool Usage").Value; root.Add(overview); VisualElement section = CreateSection("Active Timers", out VisualElement timerCard); m_SectionTitleLabel = section.ElementAt(0) as Label; m_WarningLabel = new Label(); m_WarningLabel.style.color = new Color(1f, 0.5f, 0f); m_WarningLabel.style.display = DisplayStyle.None; m_WarningLabel.style.marginBottom = 4f; timerCard.Add(m_WarningLabel); m_EmptyRow = AddTextRow(timerCard, string.Empty); m_EmptyRow.Root.style.display = DisplayStyle.None; for (int i = 0; i < MAX_DISPLAY_COUNT; i++) { m_TimerRows[i] = AddTextRow(timerCard, string.Empty); m_TimerRows[i].Root.style.display = DisplayStyle.None; } root.Add(section); RefreshContent(); } private void RefreshContent() { if (m_TimerDebugView == null) { return; } m_TimerDebugView.GetStatistics(out int activeCount, out int poolCapacity, out int peakActiveCount, out int freeCount); float poolUsage = poolCapacity > 0 ? (float)activeCount / poolCapacity : 0f; m_ActiveCountLabel.text = activeCount.ToString(); m_PoolCapacityLabel.text = poolCapacity.ToString(); m_PeakCountLabel.text = peakActiveCount.ToString(); m_FreeCountLabel.text = freeCount.ToString(); m_UsageLabel.text = Utility.Text.Format("{0:P1}", poolUsage); if (activeCount <= 0) { m_SectionTitleLabel.text = "Active Timers"; m_WarningLabel.style.display = DisplayStyle.None; m_EmptyRow.Root.style.display = DisplayStyle.Flex; m_EmptyRow.Title.text = "Status"; m_EmptyRow.Value.text = "No active timers"; SetTimerRowsVisible(0); return; } EnsureTimerInfoBuffer(activeCount); int timerCount = m_TimerDebugView.GetAllTimers(m_TimerInfos); int displayCount = timerCount > MAX_DISPLAY_COUNT ? MAX_DISPLAY_COUNT : timerCount; m_SectionTitleLabel.text = Utility.Text.Format("Active Timers ({0})", timerCount); m_EmptyRow.Root.style.display = DisplayStyle.None; if (displayCount < timerCount) { m_WarningLabel.text = Utility.Text.Format("Showing first {0} timers of {1}.", displayCount, timerCount); m_WarningLabel.style.display = DisplayStyle.Flex; } else { m_WarningLabel.style.display = DisplayStyle.None; } for (int i = 0; i < displayCount; i++) { ref RowView row = ref m_TimerRows[i]; TimerDebugInfo info = m_TimerInfos[i]; row.Title.text = Utility.Text.Format("Timer #{0}", info.TimerId); row.Value.text = Utility.Text.Format( "{0} | {1} | {2} | Remaining: {3:F2}s | Duration: {4:F2}s", info.IsLoop ? "Loop" : "Once", info.IsRunning ? "Running" : "Paused", info.IsUnscaled ? "Unscaled" : "Scaled", info.LeftTime, info.Duration); row.Root.style.display = DisplayStyle.Flex; } SetTimerRowsVisible(displayCount); } private void SetTimerRowsVisible(int visibleCount) { for (int i = 0; i < MAX_DISPLAY_COUNT; i++) { m_TimerRows[i].Root.style.display = i < visibleCount ? DisplayStyle.Flex : DisplayStyle.None; } } private RowView AddTextRow(VisualElement parent, string title) { float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f; VisualElement row = new VisualElement(); row.style.flexDirection = FlexDirection.Row; row.style.alignItems = Align.Center; row.style.minHeight = 36f * scale; row.style.marginBottom = 4f * scale; Label titleLabel = new Label(title); titleLabel.style.minWidth = 280f * scale; titleLabel.style.maxWidth = 280f * scale; titleLabel.style.color = DebuggerTheme.SecondaryText; titleLabel.style.fontSize = 18f * scale; titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; titleLabel.style.flexShrink = 0f; titleLabel.style.whiteSpace = WhiteSpace.Normal; Label valueLabel = new Label(); valueLabel.style.flexGrow = 1f; valueLabel.style.color = DebuggerTheme.PrimaryText; valueLabel.style.fontSize = 18f * scale; valueLabel.style.whiteSpace = WhiteSpace.Normal; row.Add(titleLabel); row.Add(valueLabel); parent.Add(row); RowView view; view.Root = row; view.Title = titleLabel; view.Value = valueLabel; return view; } private int EnsureTimerInfoBuffer(int count) { if (count <= 0) { if (m_TimerInfos == null || m_TimerInfos.Length == 0) { m_TimerInfos = new TimerDebugInfo[1]; } return 0; } if (m_TimerInfos == null || m_TimerInfos.Length < count) { m_TimerInfos = new TimerDebugInfo[count]; } return count; } } } }