public static void DrawLogs(Rect rect)
        {
            Widgets.DrawMenuSection(rect);

            if (!GUIController.CurrentEntry?.isPatched ?? true)
            {
                DubGUI.Heading(rect, $"Loading{GenText.MarchingEllipsis(0f)}");
                return;
            }

            var columnsR = rect.TopPartPixels(50f);

            DrawColumns(columnsR);

            columns[(int)SortBy.Average].total = 0;

            rect.AdjustVerticallyBy(columnsR.height + 4);
            rect.height -= 2f;
            rect.width  -= 2;

            Rect innerRect = rect.AtZero();

            innerRect.height = listing.curY;
            if (innerRect.height > rect.height)
            {
                innerRect.width -= 17f;
            }

            viewFrustum    = rect.AtZero();    // Our view frustum starts at 0,0 from the rect we are given
            viewFrustum.y += ScrollPosition.y; // adjust our view frustum vertically based on the scroll position

            {                                  // Begin scope for Scroll View
                Widgets.BeginScrollView(rect, ref ScrollPosition, innerRect);
                GUI.BeginGroup(innerRect);
                listing.Begin(innerRect);

                Text.Anchor = TextAnchor.MiddleCenter;
                Text.Font   = GameFont.Tiny;


                float currentListHeight = BOX_HEIGHT;

                Text.Anchor = TextAnchor.MiddleLeft;

                lock (Analyzer.LogicLock)
                {
                    foreach (ProfileLog log in Analyzer.Logs)
                    {
                        DrawLog(log, ref currentListHeight);
                    }
                }

                listing.End();
                GUI.EndGroup();
                Widgets.EndScrollView();
            }


            DubGUI.ResetFont();
        }
Exemple #2
0
        internal static Vector2 DrawLine(GraphEntry value, int prevIndex, int nextIndex, float rectHeight, float xIncrement, Color color)
        {
            float GetAdjustedY(float y, float max)
            {
                return(rectHeight - rectHeight * .95f * (y / max));
            }

            var prevY = GetAdjustedY(value.entries[prevIndex], value.max);
            var nextY = GetAdjustedY(value.entries[nextIndex], value.max);

            if (prevIndex != nextIndex - 1) // We have aliased a point (or multiple) we need to draw two lines.
            {
                var prevDrawnPoint = new Vector2(prevIndex * xIncrement, prevY);
                var prevPoint      = new Vector2((nextIndex - 1) * xIncrement, prevY);
                var curPoint       = new Vector2(nextIndex * xIncrement, nextY);
                DubGUI.DrawLine(prevDrawnPoint, prevPoint, color, 1f, true);
                DubGUI.DrawLine(prevPoint, curPoint, color, 1f, true);
            }
            else
            {
                DubGUI.DrawLine(new Vector2(prevIndex * xIncrement, prevY), new Vector2(nextIndex * xIncrement, nextY), color, 1f, true);
            }

            return(new Vector2(prevIndex * xIncrement, prevY));
        }
        public static void DrawButtons(Listing_Standard listing)
        {
            var i = 0;
            var r = new Rect();

            DrawInputIcon(ResourceCache.Strings.devoptions_input_method, CurrentInput.Method);
            DrawInputIcon(ResourceCache.Strings.devoptions_input_methodinternal, CurrentInput.InternalMethod);
            DrawInputIcon(ResourceCache.Strings.devoptions_input_methodharmony, CurrentInput.MethodHarmony);
            DrawInputIcon(ResourceCache.Strings.devoptions_input_type, CurrentInput.Type);
            DrawInputIcon(ResourceCache.Strings.devoptions_input_subclasses, CurrentInput.SubClasses);
            DrawInputIcon(ResourceCache.Strings.devoptions_input_typeharmony, CurrentInput.TypeHarmony);
            DrawInputIcon(ResourceCache.Strings.devoptions_input_assembly, CurrentInput.Assembly);

            void DrawInputIcon(string key, CurrentInput inputType)
            {
                if (i++ % 2 == 0)
                {
                    r    = listing.GetRect(Text.LineHeight + 3).LeftPart(.49f);
                    r.x += listing.ColumnWidth * .01f;
                }
                else
                {
                    r.x += listing.ColumnWidth / 2;
                }

                DubGUI.OptionalBox(r, key, () => input = inputType, input == inputType);
            }
        }
Exemple #4
0
        private static void DrawColumns(Rect rect)
        {
            Text.Anchor = TextAnchor.MiddleCenter;
            Text.Font   = GameFont.Tiny;
            GUI.color   = Color.grey;
            Widgets.DrawLineHorizontal(rect.x, rect.yMax, rect.width);
            GUI.color = Color.white;
            // [ Max ] [ Average ] [ Percent ] [ Total ] [ Calls ] [ Name ]

            DrawColumnHeader(ref rect, Strings.logs_max, Strings.logs_max_desc, SortBy.Max, NUMERIC_WIDTH, $"{totals.Max:0.000}ms");
            DrawColumnHeader(ref rect, Strings.logs_av, Strings.logs_av_desc, SortBy.Average, NUMERIC_WIDTH, $"{totals.Average:0.000}ms");

            DrawColumnHeader(ref rect, Strings.logs_percent, Strings.logs_percent_desc, SortBy.Percent, NUMERIC_WIDTH, $"{totals.Percent * 100:0.0}%");
            DrawColumnHeader(ref rect, Strings.logs_total, Strings.logs_total_desc, SortBy.Total, NUMERIC_WIDTH, $"{totals.Total:0.000}ms");

            if (GUIController.CurrentEntry.type != typeof(H_HarmonyTranspilersInternalMethods))
            {
                DrawColumnHeader(ref rect, Strings.logs_calls, Strings.logs_calls_desc, SortBy.Calls, NUMERIC_WIDTH, $"{totals.Calls.ToString("N0", CultureInfo.InvariantCulture)}");
            }
            // give the name 'infinite' width so there is no wrapping
            // Set text anchor to middle left so we can see our text
            // offset by four chars to make it look offset
            Text.Anchor = TextAnchor.MiddleLeft;
            DrawColumnHeader(ref rect, "    " + Strings.logs_name, Strings.logs_name_desc, SortBy.Name, 10000);
            DubGUI.ResetFont();
        }
        public static void Draw(Rect rect)
        {
            var innerRect = rect.AtZero();

            innerRect.height = yHeigthCache;

            viewFrustum    = rect.AtZero();
            viewFrustum.y += scrollOffset.y;

            Widgets.BeginScrollView(rect, ref scrollOffset, innerRect, false);
            GUI.BeginGroup(innerRect);
            listing.Begin(innerRect);

            float yHeight = 0;

            Text.Anchor = TextAnchor.MiddleLeft;
            Text.Font   = GameFont.Tiny;

            lock (sync)
            {
                if (!(cachedEntries.Count == 1 && cachedEntries.First() == searchText))
                {
                    foreach (var entry in cachedEntries)
                    {
                        var r = listing.GetRect(Text.LineHeight);
                        yHeight += r.height;

                        if (!r.Overlaps(viewFrustum))
                        {
                            continue;
                        }

                        if (Widgets.ButtonInvisible(r))
                        {
                            Panel_DevOptions.currentInput = entry;
                        }

                        Widgets.DrawBoxSolid(r, Modbase.Settings.GraphCol);

                        if (Mouse.IsOver(r))
                        {
                            Widgets.DrawHighlight(r);
                        }

                        r.width = 2000;
                        Widgets.Label(r, " " + entry);
                    }
                }
            }

            yHeigthCache = yHeight;

            listing.End();
            GUI.EndGroup();
            Widgets.EndScrollView();

            DubGUI.ResetFont();
        }
Exemple #6
0
        public static void Draw(Rect rect)
        {
            var row = rect.LeftPartPixels(25f);

            if (Widgets.ButtonImage(row, TexButton.SpeedButtonTextures[Analyzer.CurrentlyPaused ? 1 : 0]))
            {
                Analyzer.CurrentlyPaused = !Analyzer.CurrentlyPaused;
                GUIController.CurrentEntry.SetActive(!Analyzer.CurrentlyPaused);
            }

            TooltipHandler.TipRegion(row, Strings.top_pause_analyzer);
            rect.AdjustHorizonallyBy(25f);

            row = rect.LeftPartPixels(25);
            if (Widgets.ButtonImage(row, Textures.refresh))
            {
                GUIController.ResetProfilers();
            }

            TooltipHandler.TipRegion(row, Strings.top_refresh);

            var searchbox = rect.LeftPartPixels(rect.width - 300f);

            searchbox.x += 25f;

            DubGUI.InputField(searchbox, Strings.top_search, ref TimesFilter, DubGUI.MintSearch);

            rect.AdjustHorizonallyBy(rect.width - 250f);

            Text.Anchor = TextAnchor.UpperLeft;
            Text.Font   = GameFont.Tiny;

            var cat = GUIController.CurrentCategory == Category.Tick ? "tick" : "update";
            var str = $"{ProfileController.updateAverage:F3}ms/{cat}";

            var strLen = str.GetWidthCached();

            var periodLen = rect.LeftPartPixels(130);

            rect.AdjustHorizonallyBy(130);

            Widgets.Label(periodLen, str);


            var tpsFpsRect = rect;

            tpsFpsRect.width = 50f;
            Widgets.Label(tpsFpsRect, $"FPS: {GUIElement_TPS.FPS}");
            TooltipHandler.TipRegion(tpsFpsRect, Strings.top_fps_tip);
            tpsFpsRect.x     = tpsFpsRect.xMax + 5;
            tpsFpsRect.width = 90f;
            Widgets.Label(tpsFpsRect, $"TPS: {GUIElement_TPS.TPS}({GUIElement_TPS.TPSTarget})");
            TooltipHandler.TipRegion(tpsFpsRect, Strings.top_tps_tip);
            tpsFpsRect.x     = tpsFpsRect.xMax + 5;
            tpsFpsRect.width = 30f;
            Text.Font        = GameFont.Medium;
        }
        public static void Draw(Rect rect)
        {
            var row = rect.LeftPartPixels(25f);

            if (Widgets.ButtonImage(row, TexButton.SpeedButtonTextures[Analyzer.CurrentlyPaused ? 1 : 0]))
            {
                Analyzer.CurrentlyPaused = !Analyzer.CurrentlyPaused;
                GUIController.CurrentEntry.SetActive(!Analyzer.CurrentlyPaused);
            }

            TooltipHandler.TipRegion(row, Strings.top_pause_analyzer);
            rect.AdjustHorizonallyBy(25f);

            row = rect.LeftPartPixels(25);
            if (Widgets.ButtonImage(row, Textures.refresh))
            {
                GUIController.ResetProfilers();
            }

            TooltipHandler.TipRegion(row, Strings.top_refresh);

            var searchbox = rect.LeftPartPixels(rect.width - 220f);

            searchbox.x += 25f;

            DubGUI.InputField(searchbox, Strings.top_search, ref TimesFilter, DubGUI.MintSearch);
            //    searchbox.x = searchbox.xMax;
            //    searchbox.width = 150;
            //   GUI.color = Color.grey;
            //   Widgets.Label(searchbox, MatchType);
            //   GUI.color = Color.white;


            // bit shitty and distracting, replace with a mini graph and or an entire page dedicated to garbage if it even matters realistically now which it probably doesn't so why bother aye just keep it clean
            //row.x = searchbox.xMax + 5;
            // row.width = 130f;
            //Text.Anchor = TextAnchor.MiddleCenter;
            //Widgets.FillableBar(row, Mathf.Clamp01(Mathf.InverseLerp(H_RootUpdate.LastMinGC, H_RootUpdate.LastMaxGC, H_RootUpdate.totalBytesOfMemoryUsed)), Textures.darkgrey);
            //Widgets.Label(row, H_RootUpdate.GarbageCollectionInfo);
            //TooltipHandler.TipRegion(row, Strings.top_gc_tip);

            Text.Anchor = TextAnchor.UpperLeft;
            Text.Font   = GameFont.Tiny;

            row.width = 50f;
            row.x     = searchbox.xMax + 10;
            Widgets.Label(row, $"FPS: {GUIElement_TPS.FPS}");
            TooltipHandler.TipRegion(row, Strings.top_fps_tip);
            row.x     = row.xMax + 5;
            row.width = 90f;
            Widgets.Label(row, $"TPS: {GUIElement_TPS.TPS}({GUIElement_TPS.TPSTarget})");
            TooltipHandler.TipRegion(row, Strings.top_tps_tip);
            row.x     = row.xMax + 5;
            row.width = 30f;
            Text.Font = GameFont.Medium;
        }
        public static void Checkbox(ref Rect rect, ProfileLog log, Profiler profile, ref bool active)
        {
            var checkboxRect = new Rect(rect.x, rect.y, 25f, rect.height);

            rect.x += 25f;
            if (DubGUI.Checkbox(checkboxRect, "", ref active))
            {
                GUIController.CurrentEntry.checkBox.Invoke(null, new object[] { log });
                Modbase.Settings.Write();
            }
        }
        public static void Draw(Rect inrect, GeneralInformation?currentInformation)
        {
            if (currentInformation == null || currentInformation.Value.patches.NullOrEmpty())
            {
                return;
            }

            Text.Font   = GameFont.Tiny;
            Text.Anchor = TextAnchor.MiddleLeft;

            foreach (var patch in currentInformation?.patches)
            {
                var left  = $" {patch.patchType}";
                var right = $" {patch.modName}";

                var textHeight = Mathf.Max(Text.CalcHeight(left, inrect.width / 2), Text.CalcHeight(right, inrect.width / 2 - 5f));

                var rect = inrect.TopPartPixels(textHeight);
                inrect.y += textHeight;

                var anchor = Text.Anchor;
                Text.Anchor = TextAnchor.MiddleCenter;

                var leftRect = rect.LeftPart(.5f);
                Widgets.Label(leftRect, left);
                var rightRect = rect.RightPart(.5f);
                rightRect.x += 5;
                Widgets.Label(rightRect, right);

                Text.Anchor = anchor;

                Widgets.DrawHighlightIfMouseover(rect);

                if (Mouse.IsOver(rect))
                {
                    // todo cache tip
                    TooltipHandler.TipRegion(rect, $"Mod Name: {patch.modName}\nPatch Type: {patch.patchType}\nPatch Method: {patch.typeName}:{patch.methodName}");
                }

                if (Input.GetMouseButtonDown(1) && rect.Contains(Event.current.mousePosition)) // mouse button right
                {
                    List <FloatMenuOption> options = new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("Open In Github", () => Panel_BottomRow.OpenGithub($"{patch.typeName}.{patch.methodName}")),
                        new FloatMenuOption("Open In Dnspy (requires local path)", () => Panel_BottomRow.OpenDnspy(patch.method))
                    };

                    Find.WindowStack.Add(new FloatMenu(options));
                }
            }

            DubGUI.ResetFont();
        }
        public static void Draw(Rect rect)
        {
            if (currentProfilerInformation == null || GUIController.CurrentProfiler != null && currentProfilerInformation.Value.method != GUIController.CurrentProfiler.meth)
            {
                GetGeneralSidePanelInformation();
            }

            var statbox = rect;

            statbox.width = Panel_Tabs.width - 10;


            var pRect = rect;

            pRect.x      = statbox.xMax + 10;
            pRect.width -= statbox.xMax;
            pRect.AdjustVerticallyBy(tabRect.height);

            Widgets.DrawMenuSection(statbox);

            Panel_Stats.DrawStats(statbox, currentProfilerInformation);

            Widgets.DrawMenuSection(pRect);

            Text.Font   = GameFont.Tiny;
            Text.Anchor = TextAnchor.MiddleCenter;

            tabRect.width = Mathf.Min(100f, pRect.width / 3f);

            tabRect.x = pRect.x;
            tabRect.y = pRect.y - tabRect.height;
            Drawtab(tabRect, ProfileInfoMode.Graph, "Graph");
            tabRect.x = tabRect.xMax;
            Drawtab(tabRect, ProfileInfoMode.Patches, "Patches");
            tabRect.x = tabRect.xMax;
            Drawtab(tabRect, ProfileInfoMode.StackTrace, "Stacktrace");
            tabRect.x = tabRect.xMax;

            DubGUI.ResetFont();

            switch (ProfileInfoTab)
            {
            case ProfileInfoMode.Graph: graph.Draw(pRect.ContractedBy(1)); break;

            case ProfileInfoMode.Stats: Panel_Stats.DrawStats(pRect, currentProfilerInformation); break;

            case ProfileInfoMode.Patches: Panel_Patches.Draw(pRect, currentProfilerInformation); break;

            case ProfileInfoMode.StackTrace: Panel_StackTraces.Draw(pRect, currentProfilerInformation); break;
            }
        }
Exemple #11
0
        internal static void DrawAxis(Rect rect, float yAxis, string suffix)
        {
            var yIncrement = rect.height / 4f;

            Text.Font   = GameFont.Tiny;
            Text.Anchor = TextAnchor.UpperLeft;
            Widgets.Label(rect, suffix);
            for (var i = 1; i < 5; i++)
            {
                var box = new Rect(0, 0, 25f, Text.LineHeight);
                box.y = (i * yIncrement) - (box.height / 2f);

                Widgets.Label(box, Mathf.Round((4 - i) * (yAxis / 4f) * 100) / 100 + "");
            }
            DubGUI.ResetFont();
        }
Exemple #12
0
        public static void DrawLogs(Rect rect)
        {
            Widgets.DrawMenuSection(rect);

            if (!GUIController.CurrentEntry?.isPatched ?? true)
            {
                DubGUI.Heading(rect, $"Loading{GenText.MarchingEllipsis(0f)}");
                return;
            }

            Rect innerRect = rect.AtZero();

            innerRect.height = cachedListHeight;

            viewFrustum    = rect.AtZero();    // Our view frustum starts at 0,0 from the rect we are given
            viewFrustum.y += ScrollPosition.y; // adjust our view frustum vertically based on the scroll position

            {                                  // Begin scope for Scroll View
                Widgets.BeginScrollView(rect, ref ScrollPosition, innerRect, true);
                GUI.BeginGroup(innerRect);
                listing.Begin(innerRect);

                Text.Anchor = TextAnchor.MiddleCenter;
                Text.Font   = GameFont.Tiny;

                DrawColumns(listing.GetRect(BOX_HEIGHT));
                float currentListHeight = BOX_HEIGHT;

                Text.Anchor = TextAnchor.MiddleLeft;

                lock (Analyzer.LogicLock)
                {
                    foreach (ProfileLog log in Analyzer.Logs)
                    {
                        DrawLog(log, ref currentListHeight);
                    }
                }

                cachedListHeight = currentListHeight;

                listing.End();
                GUI.EndGroup();
                Widgets.EndScrollView();
            }

            DubGUI.ResetFont();
        }
Exemple #13
0
        private static void DrawTabs(Tab tab)
        {
            DubGUI.ResetFont();
            yOffset += 40f;

            var row = listing.GetRect(30f);

            if (tab.category == Category.Settings)
            {
                if (GUIController.GetCurrentTab == tab)
                {
                    Widgets.DrawHighlightSelected(row);
                }
                Widgets.DrawHighlightIfMouseover(row);
                if (Widgets.ButtonInvisible(row))
                {
                    tab.onClick();
                }
            }
            else
            {
                Widgets.DrawHighlightIfMouseover(row);
                Widgets.DrawTextureFitted(row.RightPartPixels(row.height), tab.collapsed ? DubGUI.DropDown : DubGUI.FoldUp, 1f);
                if (Widgets.ButtonInvisible(row))
                {
                    tab.collapsed = !tab.collapsed;
                }
            }

            row.x += 5f;
            Widgets.Label(row, tab.Label);

            TooltipHandler.TipRegion(row, tab.Tip);

            Text.Anchor = TextAnchor.MiddleLeft;
            Text.Font   = GameFont.Tiny;

            if (tab.collapsed)
            {
                return;
            }

            foreach (var entry in tab.entries)
            {
                DrawEntry(ref row, entry);
            }
        }
Exemple #14
0
        public static void Draw(Rect rect)
        {
            Rect row = rect.LeftPartPixels(25f);

            if (Widgets.ButtonImage(row, TexButton.SpeedButtonTextures[Analyzer.CurrentlyPaused ? 1 : 0]))
            {
                Analyzer.CurrentlyPaused = !Analyzer.CurrentlyPaused;
                GUIController.CurrentEntry.SetActive(!Analyzer.CurrentlyPaused);
            }

            TooltipHandler.TipRegion(row, ResourceCache.Strings.top_pause_analyzer);
            rect.AdjustHorizonallyBy(25f);

            row = rect.LeftPartPixels(25);
            if (Widgets.ButtonImage(row, ResourceCache.GUI.refresh))
            {
                GUIController.ResetProfilers();
            }

            TooltipHandler.TipRegion(row, ResourceCache.Strings.top_refresh);

            Rect searchbox = rect.LeftPartPixels(rect.width - 350f);

            searchbox.x += 25f;
            DubGUI.InputField(searchbox, ResourceCache.Strings.top_search, ref TimesFilter, DubGUI.MintSearch);
            row.x       = searchbox.xMax + 5;
            row.width   = 130f;
            Text.Anchor = TextAnchor.MiddleCenter;
            Text.Font   = GameFont.Tiny;
            Widgets.FillableBar(row, Mathf.Clamp01(Mathf.InverseLerp(H_RootUpdate.LastMinGC, H_RootUpdate.LastMaxGC, H_RootUpdate.totalBytesOfMemoryUsed)), ResourceCache.GUI.darkgrey);
            Widgets.Label(row, H_RootUpdate.GarbageCollectionInfo);
            Text.Anchor = TextAnchor.UpperLeft;
            TooltipHandler.TipRegion(row, ResourceCache.Strings.top_gc_tip);

            row.x     = row.xMax + 5;
            row.width = 50f;
            Widgets.Label(row, "FPS: " + GUIElement_TPS.FPS.ToString());
            TooltipHandler.TipRegion(row, ResourceCache.Strings.top_fps_tip);
            row.x     = row.xMax + 5;
            row.width = 90f;
            Widgets.Label(row, "TPS: " + GUIElement_TPS.TPS.ToString() + "(" + GUIElement_TPS.TPSTarget.ToString() + ")");
            TooltipHandler.TipRegion(row, ResourceCache.Strings.top_tps_tip);
            row.x     = row.xMax + 5;
            row.width = 30f;
            Text.Font = GameFont.Medium;
        }
        public static void Draw(Listing_Standard listing, float winHeight)
        {
            DubGUI.CenterText(() => listing.Label("devoptions.heading".TranslateSimple()));

            listing.GapLine(6f);

            DrawButtons(listing);
            var inputBarOffset = DisplayInputField(listing, winHeight);

            DisplaySelectionOptions(listing);

            var x = listing.curX + inputBarOffset;
            var y = listing.curY - Text.LineHeight;

            var width  = listing.ColumnWidth - inputBarOffset;
            var height = (winHeight - listing.curY) - 18f;

            var uiPoint = GUIUtility.GUIToScreenPoint(new Vector2(x, y));
            var rect    = new Rect(uiPoint, new Vector2(width, height));


            Window_SearchBar.SetCurrentInput(input);
            Window_SearchBar.UpdateSearchString(currentInput);

            bool shouldExit;

            lock (Window_SearchBar.sync)
            {
                shouldExit = Window_SearchBar.CheckShouldClose(new Rect(x, y, width, height)) || Window_SearchBar.cachedEntries.Count == 1 && Window_SearchBar.cachedEntries.First() == currentInput;
            }

            if (shouldExit)
            {
                return;
            }

            // 0x7FFFFFFF is 011111 ... in binary, in effect takes only the positive component of the hash
            Find.WindowStack.ImmediateWindow(currentInput.GetHashCode() & 0x7FFFFFFF, rect, WindowLayer.Super, () =>
            {
                GUI.color = Window_SearchBar.windowTransparency;
                Window_SearchBar.DoWindowContents(rect.AtZero());
                GUI.color = Color.white;
            }, false);
        }
Exemple #16
0
        private void DrawChangeTraceButton(Rect rect)
        {
            var height = rect.height;

            if (height > 25f + Text.LineHeight)
            {
                DubGUI.CenterText(() => Widgets.Label(rect.TopPartPixels(Text.LineHeight), "Change"));
                rect.AdjustVerticallyBy(Text.LineHeight);
            }

            var tooltip = $"Change the currently viewed stacktrace";

            if (currentTrackedStacktraces == 0)
            {
                GUI.color = Color.gray;
            }

            var centerRect = rect.CenterWithDimensions(25, 25);

            TooltipHandler.TipRegion(centerRect, tooltip);

            if (Widgets.ButtonImage(centerRect, Textures.Burger))
            {
                if (currentTrackedStacktraces != 0)
                {
                    var traces = StackTraceUtility.traces.Values.ToList();
                    CalculateHeaders(traces, traces.MaxBy(t => t.Depth).Depth);

                    var options = StackTraceUtility.traces
                                  .OrderBy(p => p.Value.Count)
                                  .Reverse()
                                  .Select(st => new FloatMenuOption($"{st.Value.Header} : {st.Value.Count}", () => { currentTrace = st.Key; }))
                                  .ToList();

                    Find.WindowStack.Add(new FloatMenu(options));
                }
            }

            if (currentTrackedStacktraces == 0)
            {
                GUI.color = Color.white;
            }
        }
Exemple #17
0
        public static void Draw(Rect rect, IEnumerable <Tab> tabs)
        {
            Rect ListerBox = rect.LeftPartPixels(width);

            ListerBox.width -= 10f;
            Widgets.DrawMenuSection(ListerBox);
            ListerBox = ListerBox.ContractedBy(4f);

            Rect baseRect = ListerBox.AtZero();

            baseRect.width -= 16f;
            baseRect.height = ListHeight;

            Text.Anchor = TextAnchor.MiddleLeft;
            Text.Font   = GameFont.Tiny;

            yOffset = 0f;

            { // Begin Scope for Scroll & GUI Group/View
                Widgets.BeginScrollView(ListerBox, ref ScrollPosition, baseRect);
                GUI.BeginGroup(baseRect);
                listing.Begin(baseRect);

                foreach (Tab tab in tabs)
                {
                    if (tab.category == Category.Modder && tab.entries.Count == 0) // if the modder tab is empty, no need to draw it
                    {
                        continue;
                    }

                    DrawTabs(tab);
                }

                listing.End();
                GUI.EndGroup();
                Widgets.EndScrollView();
            }


            DubGUI.ResetFont();
            ListHeight = yOffset;
        }
Exemple #18
0
        public static void Drawtab(Rect r, int i, string lab)
        {
            r.height += 1;
            r.width  += 1;
            Widgets.DrawMenuSection(r);
            if (PatchTab == i)
            {
                var hang = r.ContractedBy(1f);
                hang.y += 2;
                Widgets.DrawBoxSolid(hang, Widgets.MenuSectionBGFillColor);
            }

            Text.Anchor = TextAnchor.MiddleCenter;
            Text.Font   = GameFont.Tiny;
            Widgets.Label(r, lab);
            DubGUI.ResetFont();
            if (Widgets.ButtonInvisible(r))
            {
                PatchTab = i;
            }
        }
Exemple #19
0
        private static void DrawTabs(Tab tab)
        {
            DubGUI.ResetFont();
            yOffset += 40f;

            Rect row = listing.GetRect(30f);

            if (tab.category == Category.Settings)
            {
                if (Widgets.ButtonInvisible(row))
                {
                    tab.onClick();
                }
            }
            else
            {
                if (Widgets.ButtonImage(row.RightPartPixels(row.height), tab.collapsed ? DubGUI.DropDown : DubGUI.FoldUp))
                {
                    tab.collapsed = !tab.collapsed;
                }
            }
            row.x += 5f;
            Widgets.Label(row, tab.Label);

            TooltipHandler.TipRegion(row, tab.Tip);

            Text.Anchor = TextAnchor.MiddleLeft;
            Text.Font   = GameFont.Tiny;

            if (tab.collapsed)
            {
                return;
            }

            foreach (KeyValuePair <Entry, Type> entry in tab.entries)
            {
                DrawEntry(ref row, entry);
            }
        }
        private static float DrawCheckbox(ref Rect rect, MethodInfo meth)
        {
            var checkBoxWidth = 30.0f + $"Enable for {meth.Name}".GetWidthCached();
            var checkBox      = rect.LeftPartPixels(checkBoxWidth);

            if (DubGUI.Checkbox(checkBox, $"Enable for {meth.Name}", ref currentlyTracking))
            {
                if (currentlyTracking)
                {
                    Modbase.Harmony.Patch(meth, postfix: new HarmonyMethod(postfix));
                    StackTraceRegex.Reset();
                    currentTrace = "";
                    currentTrackedStacktraces = 0;
                }
                else
                {
                    Modbase.Harmony.CreateProcessor(meth).Unpatch(postfix);
                }
            }

            return(checkBoxWidth);
        }
Exemple #21
0
        private void DrawEnableButton(Rect rect, MethodInfo method, string methodString)
        {
            var tooltip = $"Enable stack trace profiling";
            var height  = rect.height;

            if (height > 25f + Text.LineHeight)
            {
                DubGUI.CenterText(() => Widgets.Label(rect.TopPartPixels(Text.LineHeight), "Enable"));
                rect.AdjustVerticallyBy(Text.LineHeight);
            }

            if (currentlyTracking)
            {
                Widgets.DrawHighlightSelected(rect);
            }

            var centerRect = rect.CenterWithDimensions(25, 25);

            TooltipHandler.TipRegion(centerRect, tooltip);

            if (Widgets.ButtonImage(centerRect, Widgets.CheckboxOnTex))
            {
                if (currentlyTracking is false)
                {
                    StackTraceUtility.Reset();
                    currentTrace = 0;
                    currentTrackedStacktraces = 0;

                    Modbase.Harmony.Patch(method, postfix: new HarmonyMethod(postfix));
                }
                else
                {
                    ThreadSafeLogger.ErrorOnce($"Can not retrace {methodString} while currently tracing", method.GetHashCode());
                }

                currentlyTracking = true;
            }
        }
        private static void DrawColumns(Rect rect)
        {
            Text.Anchor = TextAnchor.MiddleCenter;
            Text.Font   = GameFont.Tiny;
            GUI.color   = Color.grey;
            Widgets.DrawLineHorizontal(rect.x, rect.yMax, rect.width);
            GUI.color = Color.white;

            var rhs = rect.RightPartPixels(30);

            rhs.x     += 5;
            rhs.width -= 10;
            rhs.y      = rhs.center.y - 10;
            rhs.height = 20;

            foreach (var column in columns.Where(c => c.Active(GUIController.CurrentEntry.type)).OrderBy(c => c.order))
            {
                if (column.sortBy == SortBy.Name)
                {
                    Text.Anchor = TextAnchor.MiddleLeft;
                }

                DrawColumnHeader(ref rect, column);
            }

            TooltipHandler.TipRegion(rhs, "Change what columns are visible");
            if (Widgets.ButtonImage(rhs, Textures.Gear))
            {
                var opts = new List <FloatMenuOption>();
                foreach (var col in columns)
                {
                    opts.Add(new FloatMenuOption(col.Name, () => col.active = !col.active, col.active ? Widgets.CheckboxOnTex : Widgets.CheckboxOffTex, Color.gray));
                }
                Find.WindowStack.Add(new FloatMenu(opts));
            }

            DubGUI.ResetFont();
        }
        public static void DisplaySelectionOptions(Listing_Standard listing)
        {
            var box = listing.GetRect(Text.LineHeight);

            var tickBox   = box.LeftPart(.24f);
            var updateBox = box.LeftPartPixels(listing.ColumnWidth * .48f);

            updateBox.AdjustHorizonallyBy(box.width / 4);


            box.AdjustHorizonallyBy(box.width / 2);

            DubGUI.OptionalBox(tickBox, "devoptions.patchtype.tick".TranslateSimple(), () => patchType     = Category.Tick, patchType == Category.Tick);
            DubGUI.OptionalBox(updateBox, "devoptions.patchtype.update".TranslateSimple(), () => patchType = Category.Update, patchType == Category.Update);

            if (Widgets.ButtonText(box, "Patch"))
            {
                if (!string.IsNullOrEmpty(currentInput))
                {
                    ExecutePatch();
                }
            }
        }
Exemple #24
0
        public static void Draw(Rect rect, IEnumerable <Tab> tabs)
        {
            var ListerBox = rect.LeftPartPixels(width);

            ListerBox.width -= 10f;
            Widgets.DrawMenuSection(ListerBox);
            ListerBox = ListerBox.ContractedBy(4f);

            var baseRect = ListerBox.AtZero();

            baseRect.width -= 16f;
            baseRect.height = ListHeight;

            Text.Anchor = TextAnchor.MiddleLeft;
            Text.Font   = GameFont.Tiny;

            yOffset = 0f;

            Widgets.BeginScrollView(ListerBox, ref ScrollPosition, baseRect);
            GUI.BeginGroup(baseRect);
            listing.Begin(baseRect);

            foreach (var tab in tabs)
            {
                if (tab.category == Category.Settings || tab.entries.Count > 0)
                {
                    DrawTabs(tab);
                }
            }

            listing.End();
            GUI.EndGroup();
            Widgets.EndScrollView();

            DubGUI.ResetFont();
            ListHeight = yOffset;
        }
Exemple #25
0
        private void DrawDisableButton(Rect rect, MethodInfo method)
        {
            var height = rect.height;

            if (height > 25f + Text.LineHeight)
            {
                DubGUI.CenterText(() => Widgets.Label(rect.TopPartPixels(Text.LineHeight), "Disable"));
                rect.AdjustVerticallyBy(Text.LineHeight);
            }

            var tooltip = $"Disable stack trace profiling";

            if (currentlyTracking is false)
            {
                GUI.color = Color.gray;
            }

            var centerRect = rect.CenterWithDimensions(25, 25);

            TooltipHandler.TipRegion(centerRect, tooltip);

            if (Widgets.ButtonImage(centerRect, Widgets.CheckboxOffTex))
            {
                if (currentlyTracking)
                {
                    Modbase.Harmony.CreateProcessor(method).Unpatch(postfix);
                }

                currentlyTracking = false;
            }

            if (currentlyTracking is false)
            {
                GUI.color = Color.white;
            }
        }
Exemple #26
0
        private static void DrawSettings(Panel_Graph instance, ref Rect position)
        {
            // [ - Calls ] [ - Times ] [ Lines ] [ Entries ------ ] [ - Bg Col ]

            var width         = position.width;
            var currentHeight = 32;
            var currentSlice  = position.TopPartPixels(currentHeight);

            position.AdjustVerticallyBy(currentHeight);

            Text.Anchor = TextAnchor.MiddleCenter;

            // [ - Times ]
            var str          = " Times ";
            var contentWidth = 20 + str.GetWidthCached();
            var rect         = currentSlice.LeftPartPixels(contentWidth);

            currentSlice.AdjustHorizonallyBy(contentWidth);

            DrawButton(instance, rect, " Times ", 0);

            // [ - Calls ]
            str          = " Calls ";
            contentWidth = 20 + str.GetWidthCached();
            rect         = currentSlice.LeftPartPixels(contentWidth);
            currentSlice.AdjustHorizonallyBy(contentWidth);

            DrawButton(instance, rect, " Calls ", 1);

            // [ - Background ]
            str          = " Background ";
            contentWidth = 20 + str.GetWidthCached();
            rect         = currentSlice.LeftPartPixels(contentWidth);
            currentSlice.AdjustHorizonallyBy(contentWidth);

            DrawButton(instance, rect, " Background ", 2);

            Text.Anchor = TextAnchor.UpperLeft;

            // [ - Entries ]
            contentWidth = 150;
            if (currentSlice.width < contentWidth)
            {
                currentSlice = position.TopPartPixels(currentHeight);
                position.AdjustVerticallyBy(currentHeight);
            }

            rect = currentSlice.LeftPartPixels(contentWidth);
            instance.entryCount = (int)Widgets.HorizontalSlider(rect.BottomPartPixels(30f), instance.entryCount, 10, 2000, true, string.Intern($"{instance.entryCount} Entries  "));

            currentSlice.AdjustHorizonallyBy(contentWidth);


            Text.Anchor = TextAnchor.MiddleCenter;

            // [ - Show Axis ]
            str          = " Axis";
            contentWidth = str.GetWidthCached() + 30;
            if (currentSlice.width < contentWidth)
            {
                currentSlice = position.TopPartPixels(currentHeight);
                position.AdjustVerticallyBy(currentHeight);
            }

            rect = currentSlice.LeftPartPixels(contentWidth);
            DubGUI.Checkbox(rect, str, ref GraphSettings.showAxis);
            currentSlice.AdjustHorizonallyBy(contentWidth);

            // [ - Show Grid ]
            str          = " Grid " + 30;
            contentWidth = str.GetWidthCached();
            if (currentSlice.width < contentWidth)
            {
                currentSlice = position.TopPartPixels(currentHeight);
                position.AdjustVerticallyBy(currentHeight);
            }

            rect = currentSlice.LeftPartPixels(contentWidth);
            DubGUI.Checkbox(rect, str, ref GraphSettings.showGrid);
            currentSlice.AdjustHorizonallyBy(contentWidth);

            // [ - Show Max ]
            str          = " Max ";
            contentWidth = str.GetWidthCached() + 30;
            if (currentSlice.width < contentWidth)
            {
                currentSlice = position.TopPartPixels(currentHeight);
                position.AdjustVerticallyBy(currentHeight);
            }

            rect = currentSlice.LeftPartPixels(contentWidth);
            DubGUI.Checkbox(rect, str, ref GraphSettings.showMax);
            currentSlice.AdjustHorizonallyBy(contentWidth);

            // [ - Aliasing ]
            str          = " Aliasing: " + (GraphSettings.lineAliasing == 0 ? "none" : GraphSettings.lineAliasing.ToString()) + " ";
            contentWidth = str.GetWidthCached();
            if (currentSlice.width < contentWidth)
            {
                currentSlice = position.TopPartPixels(currentHeight);
                position.AdjustVerticallyBy(currentHeight);
            }

            rect = currentSlice.LeftPartPixels(contentWidth);
            if (Widgets.ButtonText(rect, str, false))
            {
                GraphSettings.lineAliasing = GraphSettings.lineAliasing switch
                {
                    7.5f => 12.5f,
                    12.5f => 0.0f,
                    0.0f => 5.0f,
                    5.0f => 7.5f,
                    _ => 0.0f
                };
            }

            currentSlice.AdjustHorizonallyBy(contentWidth);

            Text.Anchor = TextAnchor.UpperLeft;
        }
Exemple #27
0
        public static void Draw(Listing_Standard listing, Rect win)
        {
            listing.Label(Strings.settings_dnspy);
            Settings.PathToDnspy = listing.TextEntry(Settings.PathToDnspy);
            listing.Gap();
            DubGUI.LabeledSliderFloat(listing, Strings.settings_updates_per_second, ref Settings.updatesPerSecond, 1.0f, 20.0f);
            DubGUI.Checkbox(Strings.settings_logging, listing, ref Settings.verboseLogging);
            DubGUI.Checkbox(Strings.settings_disable_tps_counter, listing, ref Settings.disableTPSCounter);
            DubGUI.Checkbox("settings.debuglog".Tr(), listing, ref Settings.enableLog);

            var s    = Strings.settings_disable_cleanup;
            var rect = listing.GetRect(Text.LineHeight);

            DubGUI.Checkbox(rect, s, ref Settings.disableCleanup);
            TooltipHandler.TipRegion(rect, Strings.settings_disable_cleanup_desc);

            listing.GapLine();

            DubGUI.CenterText(() => listing.Label("devoptions.heading".Tr()));
            listing.GapLine();

            var tabs = listing.GetRect(tabRect.height);

            tabs.width = tabRect.width;

            Drawtab(tabs, 0, "Patch Tools");
            tabs.x = tabs.xMax;
            Drawtab(tabs, 1, $"Saved Patches ({Settings.SavedPatches_Tick.Count + Settings.SavedPatches_Update.Count})");
            listing.Gap(4);
            if (PatchTab == 0)
            {
                if (listing.ButtonTextLabeled("Logging cycle", patchType.ToString()))
                {
                    if (patchType == Category.Tick)
                    {
                        patchType = Category.Update;
                    }
                    else
                    {
                        patchType = Category.Tick;
                    }
                    //For if onGui gets added
                    //var list = new List<FloatMenuOption>
                    //{
                    //    new FloatMenuOption("devoptions.patchtype.tick".Tr(), () => patchType = Category.Tick),
                    //    new FloatMenuOption("devoptions.patchtype.update".Tr(), () => patchType = Category.Update)
                    //    new FloatMenuOption("devoptions.patchtype.ongui".Tr(), () => patchType = Category.OnGui)
                    //};
                    //Find.WindowStack.Add(new FloatMenu(list));
                }

                if (showSearchbox)
                {
                    Window_SearchBar.Control();
                }
                var inputR = DisplayInputField(listing);

                Window_SearchBar.SetCurrentInput(input);
                Window_SearchBar.UpdateSearchString(currentInput);

                var searchRect = listing.GetRect(Mathf.Min(listing.curY, win.height - listing.curY));

                lock (Window_SearchBar.sync)
                {
                    if (showSearchbox && !Mouse.IsOver(searchRect) && Event.current.type != EventType.MouseDown)
                    {
                        showSearchbox = false;
                    }
                    if (GUI.GetNameOfFocusedControl() == "profileinput")
                    {
                        showSearchbox = true;
                    }
                    else
                    if (Mouse.IsOver(inputR))
                    {
                        showSearchbox = true;
                    }
                }

                if (showSearchbox)
                {
                    Window_SearchBar.DoWindowContents(searchRect);
                }
            }
            else
            {
                foreach (var patch in Settings.SavedPatches_Tick.ToList())
                {
                    var row = listing.GetRect(Text.LineHeight);
                    if (Widgets.CloseButtonFor(row.LeftPartPixels(30f)))
                    {
                        Settings.SavedPatches_Tick.Remove(patch);
                    }
                    Widgets.Label(row.RightPartPixels(row.width - 30f), patch + " Tick");
                    listing.GapLine();
                }
                foreach (var patch in Settings.SavedPatches_Update.ToList())
                {
                    var row = listing.GetRect(Text.LineHeight);
                    if (Widgets.CloseButtonFor(row.LeftPartPixels(30f)))
                    {
                        Settings.SavedPatches_Update.Remove(patch);
                    }
                    Widgets.Label(row.RightPartPixels(row.width - 30f), patch + " Update");
                    listing.GapLine();
                }
            }
        }
Exemple #28
0
        public static void DrawStats(Rect inrect, GeneralInformation?currentInformation)
        {
            var stats = new LogStats();

            stats.GenerateStats();

            stats = null;

            lock (CurrentLogStats.sync)
            {
                stats = CurrentLogStats.stats;
            }

            if (stats == null)
            {
                return;
            }

            inrect = inrect.ContractedBy(4f);
            var r = inrect;

            r.height = listing.CurHeight;
            r.width  = 18;
            Widgets.BeginScrollView(inrect, ref scrolls, r);

            listing.Begin(inrect);
            Text.Font = GameFont.Tiny;

            var sb = new StringBuilder();

            if (currentInformation.HasValue)
            {
                sb.AppendLine(
                    $"Method: {currentInformation.Value.methodName}, Mod: {currentInformation.Value.modName}");
                sb.AppendLine(
                    $"Assembly: {currentInformation.Value.assname}, Patches: {currentInformation.Value.patches.Count}");

                var modLabel = sb.ToString().TrimEndNewlines();
                var rect     = listing.GetRect(Text.CalcHeight(modLabel, listing.ColumnWidth));

                Widgets.Label(rect, modLabel);
                Widgets.DrawHighlightIfMouseover(rect);

                if (Input.GetMouseButtonDown(1) && rect.Contains(Event.current.mousePosition)) // mouse button right
                {
                    var options = new List <FloatMenuOption>
                    {
                        new FloatMenuOption("Open In Github",
                                            () => Panel_BottomRow.OpenGithub(
                                                $"{currentInformation.Value.typeName}.{currentInformation.Value.methodName}")),
                        new FloatMenuOption("Open In Dnspy (requires local path)",
                                            () => Panel_BottomRow.OpenDnspy(currentInformation.Value.method))
                    };

                    Find.WindowStack.Add(new FloatMenu(options));
                }

                listing.GapLine(2f);

                sb.Clear();
            }

            sb.AppendLine($"Total Entries:".Colorize(Color.grey) + $" { stats.Entries}");
            sb.AppendLine($"Total Calls:".Colorize(Color.grey) + $" {stats.TotalCalls}");
            sb.AppendLine($"Total Time:".Colorize(Color.grey) + $" {stats.TotalTime:0.000}ms");

            sb.AppendLine($"Avg Time/Call:".Colorize(Color.grey) + $" {stats.MeanTimePerCall:0.000}ms");
            sb.AppendLine($"Avg Calls/Update:".Colorize(Color.grey) + $" {stats.MeanCallsPerUpdateCycle:0.00}");
            sb.AppendLine($"Avg Time/Update:".Colorize(Color.grey) + $" {stats.MeanTimePerUpdateCycle:0.000}ms");

            sb.AppendLine($"Median Calls:".Colorize(Color.grey) + $" {stats.MedianCalls}");
            sb.AppendLine($"Median Time:".Colorize(Color.grey) + $" {stats.MedianTime}");
            sb.AppendLine($"Max Time:".Colorize(Color.grey) + $" {stats.HighestTime:0.000}ms");
            sb.AppendLine($"Max Calls/Update:".Colorize(Color.grey) + $" {stats.HighestCalls}");

            listing.Label(sb.ToTaggedString().Trim());

            DubGUI.ResetFont();

            listing.End();

            Widgets.EndScrollView();
        }
        public static void Draw(Rect inrect, GeneralInformation?currentInformation)
        {
            inrect = inrect.ContractedBy(4);
            if (currentInformation == null || currentInformation.Value.patches.NullOrEmpty())
            {
                return;
            }

            Text.Font   = GameFont.Tiny;
            Text.Anchor = TextAnchor.MiddleLeft;

            x = 0;

            var viewrect = inrect;

            viewrect.x     += 10;
            viewrect.width -= 28;
            viewrect.height = y;

            var row = viewrect;

            row.height = 40;
            row.width  = Mathf.Max(x, viewrect.width);

            Widgets.BeginScrollView(inrect, ref scroller, viewrect);

            foreach (var patch in currentInformation?.patches)
            {
                var meth = $"{patch.typeName} : {patch.methodName}";
                row.width = meth.GetWidthCached();

                if (row.width > x)
                {
                    x = row.width;
                }

                Widgets.Label(row, meth);

                Widgets.DrawHighlightIfMouseover(row);

                if (Mouse.IsOver(row))
                {
                    TooltipHandler.TipRegion(row, $"Mod Name: {patch.modName}\nPatch Type: {patch.patchType}");
                }

                if (Input.GetMouseButtonDown(1) && row.Contains(Event.current.mousePosition)) // mouse button right
                {
                    List <FloatMenuOption> options = new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("Open In Github", () => Panel_BottomRow.OpenGithub($"{patch.typeName}.{patch.methodName}")),
                        new FloatMenuOption("Open In Dnspy (requires local path)", () => Panel_BottomRow.OpenDnspy(patch.method))
                    };

                    Find.WindowStack.Add(new FloatMenu(options));
                }

                row.y = row.yMax;

                y = row.yMax;
            }

            Widgets.EndScrollView();

            DubGUI.ResetFont();
        }
Exemple #30
0
        private static void DrawEntry(ref Rect row, KeyValuePair <Entry, Type> entry)
        {
            row = listing.GetRect(30f);
            Widgets.DrawHighlightIfMouseover(row);

            if (GUIController.CurrentEntry == entry.Key)
            {
                Widgets.DrawOptionSelected(row);
            }

            row.x   += 20f;
            yOffset += 30f;

            Widgets.Label(row, entry.Key.name);

            if (Widgets.ButtonInvisible(row))
            {
                GUIController.SwapToEntry(entry.Key.name);
            }

            if (entry.Key.isClosable)
            {
                if (Input.GetMouseButtonDown(1) && row.Contains(Event.current.mousePosition))
                {
                    List <FloatMenuOption> options = new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("Close", () => GUIController.RemoveEntry(entry.Key.name))
                    };
                    Find.WindowStack.Add(new FloatMenu(options));
                }
            }

            TooltipHandler.TipRegion(row, entry.Key.tip);

            if (GUIController.CurrentEntry == entry.Key)
            {
                bool firstEntry = true;
                foreach (KeyValuePair <FieldInfo, Setting> keySetting in entry.Key.Settings)
                {
                    if (keySetting.Key.FieldType == typeof(bool))
                    {
                        row       = listing.GetRect(30f);
                        row.x    += 20f;
                        GUI.color = Widgets.OptionSelectedBGBorderColor;
                        Widgets.DrawLineVertical(row.x, row.y, 15f);

                        if (!firstEntry)
                        {
                            Widgets.DrawLineVertical(row.x, row.y - 15f, 15f);
                        }

                        row.x += 10f;
                        Widgets.DrawLineHorizontal(row.x - 10f, row.y + 15f, 10f);
                        GUI.color = Color.white;
                        yOffset  += 30f;

                        bool cur = (bool)keySetting.Key.GetValue(null);

                        if (DubGUI.Checkbox(row, keySetting.Value.name, ref cur))
                        {
                            keySetting.Key.SetValue(null, cur);

                            GUIController.ResetProfilers();
                        }
                    }

                    if (keySetting.Value.tip != null)
                    {
                        TooltipHandler.TipRegion(row, keySetting.Value.tip);
                    }

                    firstEntry = false;
                }
            }
        }