Keeping plugin toolbar buttons in correct activation state is hard

By activation state I mean the dark box which appears behind a plugin toolbar button when you hover on it, and which stays around after you click on it.

image

The main issue is that there is no way to query whether a button is active or not. There isn’t an API like ToolbarButton:GetActive() or Toolbar.IsActive, or signal equivalents, so we are forced to rely solely on listening to ToolbarButton.Clicked and hoping nothing will change the button’s activation state under our nose.

This makes the API unnecessarily obtuse and difficult to use correctly, and is the reason for why plugin toolbar buttons tend to feel very janky when it comes to activation state (because the activation state will stop reflecting the plugin’s own state if something causes them to diverge).

Fortunately, an easy and reliable solution people can use without any API changes is to just run this code every frame:

ToolbarButton:SetActive(ShouldBeActive)

EDIT: Originally when I wrote this post I said that calling this every frame caused performance problems where the UpdateToolbars microprofiler label would take ~14-15ms every frame on my machine. I have investigated further and it turns out I was setting a different button’s ToolbarButton.Enabled property every frame, which was the real cause of the performance problem. (That particular performance issue can be trivially fixed by checking ToolbarButton.Enabled ~= ShouldBeEnabled before setting it, to avoid triggering the toolbar update unnecessarily.) So, calling ToolbarButton:SetActive() every frame appears to be a fine solution as it does not have performance problems.

7 Likes

I gave up making a plugin once after spending a day on this and being absolutely confused by how different edge cases interact.

2 Likes