public void DoGUI(HierarchyFrameDataView frameDataView, bool fetchData, ref bool updateViewLive, ProfilerViewType viewType) { using (m_DoGUIMarker.Auto()) { if (Event.current.type != EventType.Layout && m_ThreadIndexDuringLastNonLayoutEvent != threadIndexInThreadNames) { m_ThreadIndexDuringLastNonLayoutEvent = threadIndexInThreadNames; EditorGUIUtility.ExitGUI(); } InitIfNeeded(); var isSearchAllowed = string.IsNullOrEmpty(treeView.searchString) || !(m_ProfilerWindow.ProfilerWindowOverheadIsAffectingProfilingRecordingData() && ProfilerDriver.deepProfiling); var isDataAvailable = frameDataView != null && frameDataView.valid; var showDetailedView = isDataAvailable && m_DetailedViewType != DetailedViewType.None; if (showDetailedView) { SplitterGUILayout.BeginHorizontalSplit(m_DetailedViewSpliterState); } // Hierarchy view area GUILayout.BeginVertical(); if (isDataAvailable && (threadIndex != frameDataView.threadIndex || threadName != frameDataView.threadName)) { SetFrameDataView(frameDataView); } DrawToolbar(frameDataView, showDetailedView, ref updateViewLive, viewType); if (!string.IsNullOrEmpty(dataAvailabilityMessage)) { GUILayout.Label(dataAvailabilityMessage, BaseStyles.label); } else if (!isDataAvailable) { if (!fetchData && !updateViewLive) { GUILayout.Label(BaseStyles.liveUpdateMessage, BaseStyles.label); } else { GUILayout.Label(BaseStyles.noData, BaseStyles.label); } } else if (!isSearchAllowed) { GUILayout.Label(BaseStyles.disabledSearchText, BaseStyles.label); } else { var rect = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true), GUILayout.ExpandHeight(true)); m_TreeView.SetFrameDataView(frameDataView); m_TreeView.OnGUI(rect, updateViewLive); if (m_TreeView.HasSelection() && m_TreeView.proxySelectionInfo.hasProxySelection) { if (m_TreeView.proxySelectionInfo.cachedDisplayContent == null) { var diff = Math.Abs(m_TreeView.proxySelectionInfo.pathLengthDifferenceForProxy); m_TreeView.proxySelectionInfo.cachedDisplayContent = new GUIContent( BaseStyles.selectionExtraInfoHierarhcyView + string.Format( BaseStyles.proxySampleMessage, m_TreeView.proxySelectionInfo.nonProxyName, diff, diff == 1 ? BaseStyles.proxySampleMessageScopeSingular : BaseStyles.proxySampleMessageScopePlural), BaseStyles.warningTriangle.image); } GUILayout.BeginHorizontal(); GUILayout.Box(m_TreeView.proxySelectionInfo.cachedDisplayContent, BaseStyles.selectionExtraInfoArea); var rectForSampleStackButton = GUILayoutUtility.GetRect(BaseStyles.showDetailsDropdownContent, BaseStyles.tooltipDropdown, GUILayout.ExpandHeight(false), GUILayout.ExpandHeight(false)); if (GUI.Button(rectForSampleStackButton, BaseStyles.showDetailsDropdownContent, BaseStyles.tooltipDropdown)) { var selection = m_TreeView.GetSelection(); var selectedId = (selection != null && selection.Count > 0) ? selection[0] : ProfilerFrameDataHierarchyView.invalidTreeViewId; if (selectedId >= 0) { var menu = new GenericMenu(); // Show Sample Selection: var rawSampleIndices = new List <int>(frameDataView.GetItemMergedSamplesCount(selectedId)); frameDataView.GetItemRawFrameDataViewIndices(selectedId, rawSampleIndices); var actualMarkerIdPath = new List <int>(frameDataView.GetItemDepth(selectedId)); using (var iterator = new RawFrameDataView(frameDataView.frameIndex, frameDataView.threadIndex)) { string name = null; var rawIndex = ProfilerTimelineGUI.GetItemMarkerIdPath(iterator, cpuModule, rawSampleIndices[0], ref name, ref actualMarkerIdPath); } var actualMarkerPath = new List <string>(actualMarkerIdPath.Count); foreach (var id in actualMarkerIdPath) { if ((frameDataView.GetMarkerFlags(id) & Unity.Profiling.LowLevel.MarkerFlags.AvailabilityEditor) != 0) { actualMarkerPath.Add(string.Format("EditorOnly [{0}]", frameDataView.GetMarkerName(id))); } else { actualMarkerPath.Add(frameDataView.GetMarkerName(id)); } } // admittedly, it'd be nice to only generate the text if sample selection option was chosen... // however, that would need to happen in an OnGui call and not within the callback of the generic menu, // to be able to calculate the needed window size and avoid glitches on first displaying it. // at least the user already clicked on the dropdown for this... string selectedSampleStackText = null; var sampleStackSb = new System.Text.StringBuilder(); if (m_TreeView.proxySelectionInfo.nonProxySampleStack != null && m_TreeView.proxySelectionInfo.nonProxySampleStack.Count > 0) { for (int i = m_TreeView.proxySelectionInfo.nonProxySampleStack.Count - 1; i >= 0; i--) { sampleStackSb.AppendLine(m_TreeView.proxySelectionInfo.nonProxySampleStack[i]); } selectedSampleStackText = sampleStackSb.ToString(); } string actualSampleStackText = null; if (actualMarkerPath != null && actualMarkerPath.Count > 0) { sampleStackSb.Clear(); for (int i = actualMarkerPath.Count - 1; i >= 0; i--) { sampleStackSb.AppendLine(actualMarkerPath[i]); } actualSampleStackText = sampleStackSb.ToString(); } var selectionSampleStackContent = selectedSampleStackText != null ? new GUIContent(selectedSampleStackText) : null; var actualSampleStackContent = actualSampleStackText != null ? new GUIContent(actualSampleStackText) : null; var sampleStackWindowSize = SelectedSampleStackWindow.CalculateSize(selectionSampleStackContent, actualSampleStackContent); menu.AddItem(BaseStyles.showSelectedSampleStacks, false, () => { SelectedSampleStackWindow.ShowSampleStackWindow(GUIUtility.GUIToScreenRect(rectForSampleStackButton).position, sampleStackWindowSize, selectionSampleStackContent, actualSampleStackContent); }); menu.DropDown(rectForSampleStackButton); } } GUILayout.EndHorizontal(); } } GUILayout.EndVertical(); if (showDetailedView) { GUILayout.BeginVertical(); // Detailed view area EditorGUILayout.BeginHorizontal(BaseStyles.toolbar); DrawDetailedViewPopup(); GUILayout.FlexibleSpace(); cpuModule.DrawOptionsMenuPopup(); EditorGUILayout.EndHorizontal(); switch (m_DetailedViewType) { case DetailedViewType.Objects: detailedObjectsView.DoGUI(BaseStyles.header, frameDataView, m_TreeView.GetSelection()); break; case DetailedViewType.CallersAndCallees: detailedCallsView.DoGUI(BaseStyles.header, frameDataView, m_TreeView.GetSelection()); break; } GUILayout.EndVertical(); SplitterGUILayout.EndHorizontalSplit(); } HandleKeyboardEvents(); } }
protected void ShowLargeTooltip(Vector2 pos, Rect fullRect, GUIContent content, ReadOnlyCollection <string> selectedSampleStack, float lineHeight, int frameIndex, int threadIndex, bool hasCallstack, ref float downwardsZoomableAreaSpaceNeeded, int selectedSampleIndexIfProxySelection = -1) { // Arrow of tooltip var arrowRect = BaseStyles.tooltipArrowRect; arrowRect.position += pos; var style = BaseStyles.tooltip; var size = style.CalcSize(content); var copyButtonSize = BaseStyles.tooltipButton.CalcSize(BaseStyles.tooltipCopyTooltip); var sampleStackButtonSize = new Vector2(); if (selectedSampleStack != null && selectedSampleStack.Count > 0) { sampleStackButtonSize = BaseStyles.tooltipButton.CalcSize(BaseStyles.showDetailsDropdownContent); sampleStackButtonSize.x += BaseStyles.magicMarginValue; } const int k_ButtonBottomMargin = BaseStyles.magicMarginValue; var requiredButtonHeight = Mathf.Max(copyButtonSize.y, sampleStackButtonSize.y) + k_ButtonBottomMargin; // report how big the zoomable area needs to be to be able to see the entire contents if the view is big enough downwardsZoomableAreaSpaceNeeded = arrowRect.height + size.y + requiredButtonHeight; size.y += requiredButtonHeight; size.x = Mathf.Max(size.x, copyButtonSize.x + sampleStackButtonSize.x + k_ButtonBottomMargin * 3); var heightAvailableDownwards = Mathf.Max(fullRect.yMax - arrowRect.yMax, 0); var heightAvailableUpwards = Mathf.Max(pos.y - lineHeight - arrowRect.height - fullRect.y, 0); float usedHeight = 0; float rectY = 0; // Flip tooltip if too close to bottom and there's more space above it var flipped = (arrowRect.yMax + arrowRect.height + size.y > fullRect.yMax) && heightAvailableUpwards > heightAvailableDownwards; if (flipped) { arrowRect.y -= (lineHeight + 2 * arrowRect.height); usedHeight = Mathf.Min(heightAvailableUpwards, size.y); rectY = arrowRect.y + arrowRect.height - usedHeight; } else { usedHeight = Mathf.Min(heightAvailableDownwards, size.y); rectY = arrowRect.yMax; } // The tooltip needs to have enough space (on line of text + buttons) to allow for us to but buttons in there without glitches var showButtons = usedHeight >= (requiredButtonHeight + style.CalcSize(BaseStyles.tooltipCopyTooltip).y); if (!showButtons) { size.y -= requiredButtonHeight; } // Label box var rect = new Rect(pos.x + BaseStyles.tooltipArrowRect.x, rectY, size.x, usedHeight); // Ensure it doesn't go too far right if (rect.xMax > fullRect.xMax) { rect.x = Mathf.Max(fullRect.x, fullRect.xMax - rect.width); } if (rect.xMax > fullRect.xMax) { rect.xMax = fullRect.xMax; } if (arrowRect.xMax > fullRect.xMax + 20) { arrowRect.x = fullRect.xMax - arrowRect.width + 20; } // Adjust left to we can always see giant (STL) names. if (rect.xMin < fullRect.xMin) { rect.x = fullRect.xMin; } if (arrowRect.xMin < fullRect.xMin - 20) { arrowRect.x = fullRect.xMin - 20; } // Draw small arrow GUI.BeginClip(arrowRect); var oldMatrix = GUI.matrix; if (flipped) { GUIUtility.ScaleAroundPivot(new Vector2(1.0f, -1.0f), new Vector2(arrowRect.width * 0.5f, arrowRect.height)); } GUI.Label(new Rect(0, 0, arrowRect.width, arrowRect.height), GUIContent.none, BaseStyles.tooltipArrow); GUI.matrix = oldMatrix; GUI.EndClip(); var copyButtonRect = new Rect(rect.x + k_ButtonBottomMargin, rect.yMax - copyButtonSize.y - k_ButtonBottomMargin, copyButtonSize.x, copyButtonSize.y); var selectableLabelTextRect = rect; if (showButtons) { selectableLabelTextRect.yMax = copyButtonRect.y - k_ButtonBottomMargin; } var buttonArea = rect; if (showButtons) { buttonArea.yMin = copyButtonRect.y; } // Draw tooltip if (Event.current.type == EventType.Repaint) { style.Draw(rect, false, false, false, false); } GUI.BeginClip(selectableLabelTextRect); selectableLabelTextRect.position = Vector2.zero; //var controlId = GUIUtility.GetControlID(BaseStyles.tooltipButtonAreaControlId, FocusType.Passive, buttonArea); EditorGUI.SelectableLabel(selectableLabelTextRect, content.text, style); GUI.EndClip(); if (showButtons) { // overwrite the mouse cursor for the buttons on potential splitters underneath the buttons if (Event.current.type == EventType.Repaint) { EditorGUIUtility.AddCursorRect(buttonArea, MouseCursor.Arrow); } // and steal the hot control if needed. var controlId = GUIUtility.GetControlID(BaseStyles.tooltipButtonAreaControlId, FocusType.Passive, buttonArea); var eventType = Event.current.GetTypeForControl(controlId); if (eventType == EventType.MouseDown && buttonArea.Contains(Event.current.mousePosition)) { GUIUtility.hotControl = controlId; } else if (GUIUtility.hotControl == controlId && eventType == EventType.MouseUp) { GUIUtility.hotControl = 0; } // copyButton if (GUI.Button(copyButtonRect, BaseStyles.tooltipCopyTooltip, BaseStyles.tooltipButton)) { Clipboard.stringValue = content.text; } if (selectedSampleStack != null && selectedSampleStack.Count > 0) { var sampleStackRect = new Rect(copyButtonRect.xMax + k_ButtonBottomMargin, copyButtonRect.y, sampleStackButtonSize.x, sampleStackButtonSize.y); if (EditorGUI.DropdownButton(sampleStackRect, BaseStyles.showDetailsDropdownContent, FocusType.Passive, BaseStyles.tooltipDropdown)) { var menu = new GenericMenu(); // The tooltip is already pointing to the what's currently selected, switching the view will Apply that selection in the other view menu.AddItem(GetCPUProfilerViewTypeName(ProfilerViewType.Hierarchy), false, () => { viewTypeChanged(ProfilerViewType.Hierarchy); }); menu.AddItem(GetCPUProfilerViewTypeName(ProfilerViewType.RawHierarchy), false, () => { viewTypeChanged(ProfilerViewType.RawHierarchy); }); menu.AddSeparator(""); if (hasCallstack) { menu.AddItem(BaseStyles.showFullDetailsForCallStacks, showFullDetailsForCallStacks, () => showFullDetailsForCallStacks = !showFullDetailsForCallStacks); } else { menu.AddDisabledItem(BaseStyles.showFullDetailsForCallStacks, showFullDetailsForCallStacks); } menu.AddSeparator(""); var rectWindowPosition = EditorGUIUtility.GUIToScreenRect(sampleStackRect).position; // Show Sample Selection : // admittedly, it'd be nice to only generate the text if sample selection option was chosen... // however, that would need to happen in an OnGui call and not within the callback of the generic menu, // to be able to calculate the needed window size and avoid glitches on first displaying it. // at least the user already clicked on the dropdown for this... string selectedSampleStackText = null; var sampleStackSb = new System.Text.StringBuilder(); for (int i = selectedSampleStack.Count - 1; i >= 0; i--) { sampleStackSb.AppendLine(selectedSampleStack[i]); } selectedSampleStackText = sampleStackSb.ToString(); string actualSampleStackText = null; if (selectedSampleIndexIfProxySelection >= 0) { using (var frameData = ProfilerDriver.GetRawFrameDataView(frameIndex, threadIndex)) { if (frameData.valid) { sampleStackSb.Clear(); string sampleName = null; var markerIdPath = new List <int>(selectedSampleStack != null ? selectedSampleStack.Count : 10); if (ProfilerTimelineGUI.GetItemMarkerIdPath(frameData, cpuModule, selectedSampleIndexIfProxySelection, ref sampleName, ref markerIdPath) >= 0) { for (int i = markerIdPath.Count - 1; i >= 0; i--) { sampleStackSb.AppendLine(frameData.GetMarkerName(markerIdPath[i])); } actualSampleStackText = sampleStackSb.ToString(); } } } } var selectionSampleStackContent = selectedSampleStack != null ? new GUIContent(selectedSampleStackText) : null; var actualSampleStackContent = actualSampleStackText != null ? new GUIContent(actualSampleStackText) : null; var sampleStackWindowSize = SelectedSampleStackWindow.CalculateSize(selectionSampleStackContent, actualSampleStackContent); menu.AddItem(BaseStyles.showSelectedSampleStacks, false, () => { SelectedSampleStackWindow.ShowSampleStackWindow(rectWindowPosition, sampleStackWindowSize, selectionSampleStackContent, actualSampleStackContent); }); menu.DropDown(sampleStackRect); } } } }