Here's an idea. I've begun implementing it.

Tell me why it's dumb, or what its shortcomings are.

Put all your logic inside an App.logic (which will get called from
App.run and wrapped with the stuff it needs). Afterward, call App.run
itself.

You automatically get one toplevel window, called @main in App.
Everything in App.logic defaults to this as a container.

When you create a container, you put a block on the constructor. Any
widget (container or other) created in that block will belong to the
current container. So the nesting of the block structure reflects the
nesting of the containers.

When you create a non-container, you put a block on the constructor.
It will be associated with the "default" (most common) message for
that widget.

Here's some code. It works, for what it's worth.

     require 'ez-gtk'
     include EZ_GTK

     def App.logic
       @main.title = "My window"
       @hb = HBox.new do
         @vb1 = VBox.new do
           @btn1 = Button.new("Do it") { puts "pressed btn1" }
           @btn2 = Button.new("This too") { puts "pressed btn2" }
         end
         @vb2 = VBox.new do
           @btn3 = Button.new("Third button") { puts "pressed btn3" }
         end
       end
     end

     App.run

Please comment on this before asking to see ez-gtk itself.  ;)


Thanks,
Hal