But a stateful GUI is not a sin: the program as a whole has state, application code needs to process GUI inputs to update application-level state (e.g. currently set game options) while tracking mechanical details like whether a button is "half clicked" is suitable for automation at the GUI library level.
Regarding IDs, they can (and must) be provided by client code, as client code is responsible for managing widget identity (i.e. whether the button that might have a mouse up event this frame is "the same" that received a mouse down event some frames ago).
In C or C++, string names could usually be autogenerated with trivial macros relying on __LINE__ and __FILE__.