Mac-Compatible way to add editor menus

In the past, I have added custom menus to the grasshopper editor in various plug-ins. I have done this by setting an event handler for GrasshopperInstances.CanvasCreated in a custom PriorityLoad which then grabs the DocumentEditor and adds things to its MainMenuStrip. However, as I recall this seems to break plug-in load on mac Grasshopper. Is there an alternate pathway to adding a menu that’s cross-platform? Or any mac-compatible way that’s accessible from a plugin?

Anybody? @dan @curtisw?

Yes I remember someone left me a comment saying they cannot see the menu. I thought it was a per-machine glitch as the menu popped up when I tested on Mac, like half a year ago. :thinking: Have you figured out why?

Hey @andheum,

Could you provide an example of what you have done to modify the menu? We can probably get it to work the same on Mac, though you may have to account for a slightly different menu structure. There is probably just something missing or tweaks needed to support it in our custom Windows Forms implementation.

using Grasshopper.GUI;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Special;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace MetaHopper
{
    public class MH_AssemblyPriority : GH_AssemblyPriority
    {
        public override GH_LoadingInstruction PriorityLoad()
        {
            Grasshopper.Instances.ComponentServer.AddAlias("col", new Guid("03AD926B-5642-402E-88B2-14F752595928"));
            Grasshopper.Instances.ComponentServer.AddCategoryIcon("MetaHopper", Properties.Resources.metahopper16);

#if !MACOS
            Grasshopper.Instances.CanvasCreated += RegisterNewMenuItems;
#endif
            return GH_LoadingInstruction.Proceed;
        }

#if !MACOS
        void RegisterNewMenuItems(Grasshopper.GUI.Canvas.GH_Canvas canvas)
        {
            Grasshopper.Instances.CanvasCreated -= RegisterNewMenuItems;

            GH_DocumentEditor docEditor = Grasshopper.Instances.DocumentEditor;
            if (docEditor != null)
            {
                SetupMetaHopperMenu(docEditor);
            }

        }

        private void SetupMetaHopperMenu(GH_DocumentEditor docEditor)
        {
            ToolStripMenuItem metaHopperToolStripMenuItem;
            metaHopperToolStripMenuItem = new ToolStripMenuItem();
            docEditor.MainMenuStrip.SuspendLayout();

            docEditor.MainMenuStrip.Items.AddRange(new ToolStripItem[] {
            metaHopperToolStripMenuItem});

            // 
            // metaHopperToolStripMenuItem
            // 
            metaHopperToolStripMenuItem.DropDownItems.AddRange(MetaHopperMenuItems.ToArray());
            metaHopperToolStripMenuItem.Name = "metaHopperToolStripMenuItem";
            metaHopperToolStripMenuItem.Size = new System.Drawing.Size(125, 29);
            metaHopperToolStripMenuItem.Text = "MetaHopper";


            docEditor.MainMenuStrip.ResumeLayout(false);
            docEditor.MainMenuStrip.PerformLayout();

            GH_DocumentEditor.AggregateShortcutMenuItems += GH_DocumentEditor_AggregateShortcutMenuItems;
        }

        void GH_DocumentEditor_AggregateShortcutMenuItems(object sender, GH_MenuShortcutEventArgs e)
        {

            e.AppendItem(bestPracticizeSelectionToolStripMenuItem);
            e.AppendItem(saveSnippetToolStripMenuItem);
            e.AppendItem(bottleneckNavigatorToolStripMenuItem);
            e.AppendItem(increaseScribbleToolStripMenuItem);
            e.AppendItem(decreaseScribbleToolStripMenuItem);
            e.AppendItem(labelSelectedGroupMenuItem);
            e.AppendItem(saveIncrementalMenuItem);
        }

        ToolStripMenuItem bestPracticizeSelectionToolStripMenuItem;
        ToolStripMenuItem saveSnippetToolStripMenuItem;
        ToolStripMenuItem bottleneckNavigatorToolStripMenuItem;
        ToolStripMenuItem increaseScribbleToolStripMenuItem;
        ToolStripMenuItem decreaseScribbleToolStripMenuItem;
        ToolStripMenuItem labelSelectedGroupMenuItem;
        private ToolStripMenuItem saveIncrementalMenuItem;

        private List<ToolStripMenuItem> MetaHopperMenuItems
        {
            get
            {

                List<ToolStripMenuItem> list = new List<ToolStripMenuItem>();

                bestPracticizeSelectionToolStripMenuItem = new ToolStripMenuItem();
                saveSnippetToolStripMenuItem = new ToolStripMenuItem();
                bottleneckNavigatorToolStripMenuItem = new ToolStripMenuItem();
                increaseScribbleToolStripMenuItem = new ToolStripMenuItem();
                decreaseScribbleToolStripMenuItem = new ToolStripMenuItem();
                labelSelectedGroupMenuItem = new ToolStripMenuItem();
                saveIncrementalMenuItem = new ToolStripMenuItem();

                // 
                // bestPracticizeSelectionToolStripMenuItem
                // 
                bestPracticizeSelectionToolStripMenuItem.Name = "bestPracticizeSelectionToolStripMenuItem";
                bestPracticizeSelectionToolStripMenuItem.Size = new System.Drawing.Size(265, 30);
                bestPracticizeSelectionToolStripMenuItem.Text = "BestPracticize Selection";
                bestPracticizeSelectionToolStripMenuItem.Click += new System.EventHandler(bestPracticizeSelectionToolStripMenuItem_Click);
                // 
                // saveSnippetToolStripMenuItem
                // 
                saveSnippetToolStripMenuItem.Name = "saveSnippetToolStripMenuItem";
                saveSnippetToolStripMenuItem.Size = new System.Drawing.Size(265, 30);
                saveSnippetToolStripMenuItem.Text = "Save Snippet...";
                saveSnippetToolStripMenuItem.Click += new System.EventHandler(saveSnippetToolStripMenuItem_Click);

                // LaunchBottleneckNavigatorToolStripMenuItem
                // 
                bottleneckNavigatorToolStripMenuItem.Name = "BottleneckNavigatorToolStripMenuItem";
                bottleneckNavigatorToolStripMenuItem.Size = new System.Drawing.Size(265, 30);
                bottleneckNavigatorToolStripMenuItem.Text = "Launch Bottleneck Navigator...";
                bottleneckNavigatorToolStripMenuItem.Click += new System.EventHandler(bottleneckNavigatorToolStripMenuItem_Click);

                //IncreaseScribble
                increaseScribbleToolStripMenuItem.Name = "IncreaseScribble";
                increaseScribbleToolStripMenuItem.Size = new System.Drawing.Size(265, 30);
                increaseScribbleToolStripMenuItem.Text = "Increase Selected Scribble Size";
                increaseScribbleToolStripMenuItem.Click += new System.EventHandler(IncreaseScribbleSizeToolStripMenuItem_Click);

                //DecreaseScribble
                decreaseScribbleToolStripMenuItem.Name = "DecreaseScribble";
                decreaseScribbleToolStripMenuItem.Size = new System.Drawing.Size(265, 30);
                decreaseScribbleToolStripMenuItem.Text = "Decrease Selected Scribble Size";
                decreaseScribbleToolStripMenuItem.Click += new System.EventHandler(DecreaseScribbleSizeToolStripMenuItem_Click);

                //Label Groups
                labelSelectedGroupMenuItem.Name = "LabelGroups";
                labelSelectedGroupMenuItem.Size = new Size(265, 30);
                labelSelectedGroupMenuItem.Text = "Label Selected Groups with Scribbles";
                labelSelectedGroupMenuItem.Click += new EventHandler(LabelSelectedGroups);

                //Label Groups
                saveIncrementalMenuItem.Name = "SaveIncremental";
                saveIncrementalMenuItem.Size = new Size(265, 30);
                saveIncrementalMenuItem.Text = "Save and Archive";
                saveIncrementalMenuItem.Click += new EventHandler(SaveAndArchive_Click);

                list.Add(bestPracticizeSelectionToolStripMenuItem);
                list.Add(saveSnippetToolStripMenuItem);
                list.Add(bottleneckNavigatorToolStripMenuItem);
                list.Add(increaseScribbleToolStripMenuItem);
                list.Add(decreaseScribbleToolStripMenuItem);
                list.Add(labelSelectedGroupMenuItem);
                list.Add(saveIncrementalMenuItem);

                return list;
            }
        }

        private BottleneckNavigator navigator;

        private void bottleneckNavigatorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (navigator == null)
            {
                navigator = new BottleneckNavigator();
            }

            navigator.SetDoc(Grasshopper.Instances.ActiveCanvas.Document);
            navigator.Clear();
            navigator.Initialize();
            navigator.Show();
        }

        private void IncreaseScribbleSizeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            changeSelectedScribbleSize(10);
        }

        private void DecreaseScribbleSizeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            changeSelectedScribbleSize(-10);
        }


        private void changeSelectedScribbleSize(int change)
        {
            var scribbles = Grasshopper.Instances.ActiveCanvas.Document.SelectedObjects().OfType<GH_Scribble>();
            foreach (GH_Scribble s in scribbles)
            {

                s.Font = GH_FontServer.NewFont(s.Font.FontFamily.Name, (Single)s.Font.Size + change, FontStyle.Regular);

                SetScribble.RecomputeLayout(s);
                s.Attributes.ExpireLayout();
            }
            Grasshopper.Instances.ActiveCanvas.Refresh();
        }
        private void LabelSelectedGroups(object sender, EventArgs e)
        {
            var groups = Grasshopper.Instances.ActiveCanvas.Document.SelectedObjects().OfType<GH_Group>().ToList();
            LabelGroups.LabelGroupsFunction(groups);

        }
        private void SaveAndArchive_Click(object sender, EventArgs e)
        {
            SaveIncremental.Save();

        }


        private void bestPracticizeSelectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GH_Document doc = Grasshopper.Instances.ActiveCanvas.Document;
            if (doc.SelectedObjects().Count == 0)
            {
                MessageBox.Show("To use this feature, first select a group containing components. These components must be connected to some other component or params outside of the group.");
                return;
            }
            List<GH_Group> groups = doc.SelectedObjects().Where(o => o is GH_Group).Cast<GH_Group>().ToList();

            BestPracticizer.BestPracticize(doc, groups);
            doc.ScheduleSolution(4);
        }

        private void saveSnippetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CreateSnippet.SaveThisSnippet(Grasshopper.Instances.ActiveCanvas.Document);
        }

        public GH_DocumentEditor.AggregateShortcutMenuItemsEventHandler handleThis { get; set; }
        #endif
    }
}
3 Likes

Thanks, @andheum. I’ve created RH-60045 to look into getting that working.

Thanks!

Hey @andheum,

Could be some recent updates, but with your existing code (minus the stuff I don’t have to make it compile), it appears to work just fine with 6.29. Could you give it a try?

I had a go at installing MetaHopper (and Pancake too!) on OS Mojave today at work any neither would show up- I assume related to the menu issue.

I fixed this already but haven’t released the cross-platform version. In the meantime I think there’s a “metahopper Mac” on the package manager

:joy: I haven’t got time to borrow a Mac and test new code. will do

Thanks Andrew, I’ve installed the Mac compatible version. Thanks for getting back to me, love your plugins.