After much neglect, I’ve finally decided to root out how plugins actually work. Here are some behaviors I’ve uncovered:
Plugin:Activate() fires Plugin.Deactivation for every plugin, including itself.
Button:SetActive(true) will make every other button inactive. By the way, there's no way to know whether a button is active.
If a button is active (Button:SetActive(true)), then Button:SetActive(false) will fire Plugin.Deactivation for the button's associated plugin.
The only way to deactivate a plugin is to activate another plugin.
The real problem here is that how plugins should interact with each other isn’t well-defined. Since I like speculating, here’s how I would improve the plugin API:
Plugin.Active property: A bool that sets whether the plugin is in an active state. If set to true, this will fire the Deactivate event for every other plugin.
Plugin.Deactivate event: Fires after any other plugin activates. This is basically a message from the activating plugin telling this plugin that it may want to deactivate. Perhaps the name of the activating plugin can be passed as an argument.
Button.Active property: A bool that sets whether the button appears as selected.
The point here is that plugins have a way to communicate with each other for determining who should be active, the plugin’s state is exposed to the plugin script, and the script gets to choose what state the plugin is in. For easier backwards compatibility, perhaps the following bits can be added:
Plugin.AutoDeactivate: If true, Plugin.Active will be set to false when Plugin.Deactivate fires. This recreates the behavior of how plugins currently work.
Button.AutoDeactivate: If true, Button.Active will be set to false if the button's associated plugin deactivates. This is also similar to how it currently works (or how it should, at least).
I think the intent here is that Buttons are supposed to represent “tools” like the native dragger, rotate, resize tools etc. As it only makes sense for one such tool to be active at any time, buttons follow that behaviour as well. Plugins, then, are considered active if one of their buttons is (though that raises the question of why Plugin:Activate() exists at all).
But the “including itself” and “there’s no way to know whether a button is active” parts are obvious oversights/bugs.
I’ve always seen it more as a way to put toolbars and buttons in the studio UI, and I suspect that was the original intent. If you only consider how the API functions, its intended use as tools starts to make sense. But the way things are named suggests that its usage isn’t so specific. That is, instead of “Toolsets” and “Tools”, you have “Plugins” and “Buttons”.