WISH: Grasshopper Components' tooltip grafted list preview

By hovering the mouse over a component input or output we can display a shortened list of elements. In the case of flat lists we have the first few items and the last item. In the case of trees, we have the first few tree branches paths and the last one displayed. A very common case is that tree paths have only one element, in this case we will not have the values ​​displayed, only paths. I think it would be a great quality of life improvement if the preview displayed the first item for each list.

There should still be some indication that we are only seeing the first of many items, but I am sure that it could be displayed in a way that would be unambiguous and understandable.

This would avoid countless panels being connected just to quickly glance at the values ​​inside grafted trees.

One workaround for this problem is Sunglasses’ “Riched capsules” but this also requires a lot of zooming in and out while working.

In short… a slightly better tooltip would make the work more enjoyable and the definitions less cluttered.

1 Like

well, as a workaround it can be achieved by hooking GH_Param<T>.VolatileDataDescription() / GH_Attributes<IGH_Param>.SetupTooltip().

For example:

It involves tons of reverse enginnering, so I suppose McNeel won’t like it. :wink: It’s one thing I love about Grasshopper though, as you can customize it easily to a deep level.

using Grasshopper.GUI;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

namespace PancakeDev.Replacement;

internal static class DetailedTooltip
{
    public static bool Hook()
    {
        var harmony = Hub.Instance;

        var mOriginal = AccessTools.Method(typeof(GH_Attributes<IGH_Param>), "SetupTooltip"); // if possible use nameof() here
        var mPrefix = AccessTools.Method(typeof(DetailedTooltip), "SetupTooltip");
        harmony.Patch(mOriginal, prefix: new HarmonyMethod(mPrefix));
        return true;
    }
    internal static bool SetupTooltip(object __instance, PointF point, GH_TooltipDisplayEventArgs e)
    {
        if(__instance is not GH_Attributes<IGH_Param> attr) return true;

        var owner = attr.Owner;
        var desc = AlternativeGetInstanceDescription(owner);

        if (desc is null) return true;

        e.Description = desc;
///        ... GH's original code ...

        return false;
    }

    private static string? AlternativeGetInstanceDescription(IGH_Param param)
    {
///        ... GH's original code ...

        var data = param.VolatileData;
        var vd = VolatileDataDescription(data);

        if (vd is null) return null;

///        ... GH's original code ...
            _ => volatileDataCount switch
            {
                1 => $"1 value inherited from {param.SourceCount} sources…" + Environment.NewLine + vd,
                _ => $"{volatileDataCount} values inherited from {param.SourceCount} sources…" + Environment.NewLine + vd,
            },
        };
    }

    private static string? VolatileDataDescription(IGH_Structure m_data)
    {
///        ... GH's original code ...
                AppendBranch(list, m_data, j);
///        ... GH's original code ...

            return string.Join(Environment.NewLine, list);
        }
    }

    static void AppendBranch(List<string> list, IGH_Structure m_data, int index)
    {
        var branch = m_data.get_Branch(index);
        var dataCount = branch.Count;
        var str = $"{m_data.get_Path(index)} (N = {dataCount})";
        var appendStr = " ";

        if (dataCount == 0)
        {
            list.Add(str);
        }
        else
        {
            const int LengthLimit = 30;
            const int QuantityLimit = 3;

            for (var i = 0; i < dataCount; i++)
            {
                if (i >= QuantityLimit)
                {
                    appendStr += ", ...";
                    break;
                }

                if (i != 0)
                {
                    appendStr += ", ";
                }

                try
                {
                    appendStr += branch[i]?.ToString() ?? "Null";
                }
                catch
                {
                    appendStr += "??";
                }

                if (appendStr.Length > LengthLimit)
                {
                    appendStr = appendStr.Substring(0, LengthLimit);
                    appendStr += " ...";
                    break;
                }
            }

            list.Add(str + appendStr);
        }
    }
}

2 Likes