Check if Sub Menu already exists


#1

Is there anyway to check if a menu already exists?

For example I want to check if a menu named “MyAddon” exists. If it does add a sub menu to it. If not create it and then add a sub menu.

I have tried:

plugins_menu = UI.menu('Extensions')
sub_menu = UI.menu('MyAddon')
if sub_menu == nil
    sub_menu = plugins_menu.add_submenu('MyAddon')
end

but this does not seem to work.


Create submenu in Plugins menu
#2

Sorry, no, but UI element introspection methods have not yet been added to the API.


You cannot use UI.menu() to get references to submenus.

"Error: #<ArgumentError: Unknown menu myaddon>"

It can only be used to get one of the top level menus on the "Application" menubar.

When you get references to UI menu objects they are temporary. (Weirdly each time you call UI.menu() with the same name argument, the SketchUp API returns a new object, with a unique object id.)


The add_submenu() method is the only current way to get a reference to any submenu.
So this means you must get your reference when you create the submenu, and assign it to some semi-persistent variable. This means not a local variable inside a method. I usually use a class or module @@submenu variable.
But even a module var or also a constant will not survive past the evaluation of the current file set. The SketchUp C side of the API garbage collects them quite quickly.

Lets say you want to put all of your series of plugins on some “company” branded submenu (of the “Extensions” menu.) [This is a common desire.] The problem is that once the first plugin creates the submenu, and the evaluation of it’s files completes, … successive extensions cannot use the submenu reference because it is now invalid.

You’d think that perhaps they’d have written the add_submenu() method to be smart enough to return the existing submenu, rather than just stupidly create a new one with the same name. But no joy. Currently it just adds a new submenu.


This all means that you need to think ahead, and create a menu (and/or toolbar) creation facility, that handles the task for all of your extensions.

The 1st step is having the individual extensions create UI::Command objects rather than directly assign procs to menuitems.

2nd is to push references to these commands into a hash structure (possibly nested just as you’d want the menus to be nested.)

3rd each extension’s menuhash structure is pushed into your common menubuilder’s hash.

Then later “at the right time”, ie, when the loading of all extensions is complete, the menubuilder creates your nested menu structure.


In the old days, we’d use a UI.start_timer hack to trigger the menu loading. But recently a expectsStartupModelNotifications callback was added to the AppObserver class that could probably serve as a better trigger.

An alternative is for you to get a array of loader files from the Plugins folder beginning with your namespace and an underscore. See standard Ruby Dir.glob method. Every few seconds a timer checks $" (aka the $LOADED_FEATURES) array, and removes files from the list array as they appear in $", ie, as they are reported loaded. When the list of your extensions is done loading, the menubuilding is triggered.
This way your menu might stil appear in alpha order rather then at the end of the “Extensions” menu.


#3

Of course since you now know how to use Fiddle, you could use WinAPI calls instead.