The enabled state of a GAction now propagates both to any toolbar buttons
and any menu items linked to the action. Toolbar buttons are painted in
a grayed out style when disabled. Menu items are gray when disabled. :^)
That first design was the wrong idea. Instead, have VBWidget instantiate
a GWidget of the appropriate type and parent it to the VBForm.
We then use a new "greedy hit-testing" mechanism in GWidget to prevent any
mouse events from reaching the VBForm's children.
To paint the grabbers above the child widgets, I added a slightly hackish
but kind of neat second_paint_event() that is called after a widget has
painted all of his children. :^)
Only process paint and resize events on the GUI client side if those events
have the latest up-to-date window size. This drastically reduces async
overdraw during interactive resize.
The WindowServer still holds on to at least one backing store in case it
needs to paint us again before we can render a new one. This ensures that
we don't end up stuck with an undersized backing store.
When resizing a window, we often end up having to paint some part of it
without coverage in the current backing store. This patch makes those cases
look nicer by having a fallback background color for each window, passed
along with the CreateWindow client message.
This is essentially a combo widget containing a single-line GTextEditor
and two buttons for increment and decrement. The GTextEditor::on_change
callback is hooked to prevent non-numeric input but it's not entirely
perfect since that callback is asynchronous. This will work until we have
some more sophisticated input validation mechanism though.
Any GWidget can have a tooltip and it will automatically pop up below the
center of the widget when hovered. GActions added to GToolBars will use
the action text() as their tooltip automagically. :^)
This code can get a bit confused when the child is destroyed before we
handle the ChildRemoved event. In those cases, the GChildEvent::child()
getter will return nullptr as it's backed by a WeakPtr.
To work around this issue, just always invalidate the layout for now.
This can be made a lot tighter in the future.
These events are identical, so it's silly to send both. Just broadcast
window state changes everywhere instead, it doesn't matter when it was
added as clients are learning about this asynchronously anyway.